From a8970c1f18123d0fbc50580b8f583c7c466dafc3 Mon Sep 17 00:00:00 2001 From: German Khokhlov Date: Wed, 11 Nov 2026 13:40:12 +0400 Subject: [PATCH] Street Level: a geocoder that throws fails show_street_level at its location stage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A provider that threw while geocoding (offline, an upstream down) escaped the action as a bare error. show_street_level now reports it as its location stage ("Could not arrive at …", the cause in detail), and nothing is switched or looked up; fly_to_location itself is unchanged. --- src/voice/gevActions.js | 35 ++++++++++++++++++++++------------- src/voice/gevActions.test.mjs | 19 +++++++++++++++++++ 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/voice/gevActions.js b/src/voice/gevActions.js index 5d021ef6e..14956c6c9 100644 --- a/src/voice/gevActions.js +++ b/src/voice/gevActions.js @@ -3961,19 +3961,28 @@ async function requestStreetLevel( if (atViewCentre || (args.fly === false && hasCoordinates)) { if (hasCoordinates) point = { lat: latitude, lon: longitude }; } else { - location = await runGevAction( - 'fly_to_location', - { - ...(entity - ? { entity } - : hasCoordinates - ? { latitude, longitude } - : { query }), - viewMode: 'close', - waitForArrival: true, - }, - runOptions, - ); + try { + location = await runGevAction( + 'fly_to_location', + { + ...(entity + ? { entity } + : hasCoordinates + ? { latitude, longitude } + : { query }), + viewMode: 'close', + waitForArrival: true, + }, + runOptions, + ); + } catch (error) { + // A geocoder that throws (offline, a provider down) is this stage's + // failure too, not an error past the action. + return fail('location', `Could not arrive at ${label}`, { + cancelled: !current(), + detail: String(error?.message || error), + }); + } if (location?.ok !== true || !current()) return fail( 'location', diff --git a/src/voice/gevActions.test.mjs b/src/voice/gevActions.test.mjs index 7ccaabc8d..64e36c42b 100644 --- a/src/voice/gevActions.test.mjs +++ b/src/voice/gevActions.test.mjs @@ -7005,6 +7005,25 @@ test('show_street_level tells a failed enable, a delisted source, a key refused assert.deepEqual(unconfigured.order, [], 'nothing changed'); }); +test('show_street_level reports a geocoder that throws as its location stage', async () => { + globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null }; + const h = createStreetLevelHarness({ enabled: true, activeStack: 'osm' }); + const runner = createGevActionRunner({ + viewer: createVoiceNavigationHarness().viewer, + styleManager: { mapStackController: { getActiveId: () => 'osm' }, setMapStack: async () => ({ ok: true }) }, + dataManager: h.dataManager, + placeSearch: { + geocode: async () => { + throw new Error('geocoder unreachable'); + }, + }, + }); + const result = await runner('show_street_level', { query: 'Capitol Mall', provider: 'mapillary' }); + assert.deepEqual([result.ok, result.stage], [false, 'location']); + assert.equal(result.error, 'Could not arrive at Capitol Mall'); + assert.deepEqual(h.order.filter((step) => /^(params|open|arm)/.test(step)), [], 'nothing switched or looked up'); +}); + test('show_street_level reports a flight that lands without a position', async () => { globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null }; const hit = createStreetLevelHarness({ enabled: true, activeStack: 'osm' });