From 019f61ab767e173d12bea6ef8a0259097278b2b5 Mon Sep 17 00:00:00 2001 From: Glenn Gore Date: Wed, 5 Aug 2026 23:09:13 +0200 Subject: [PATCH] fix(inbound): a throw while handling an inbound message is no longer silent handleInbound wrapped dispatchInbound in try/finally with no catch, and the call site is `void handleInbound(...)`. So anything dispatch threw -- rather than returned as a refusal -- became an unhandled rejection: no log from this file, no prompt, no decision sent to the executor, and the message already acked to the mediator so its queued copy was gone. That is indistinguishable from a request that never arrived, and it is exactly what it looked like: `[pnm inbound] received` followed by silence. Dispatching the identical message by hand from the offscreen console raised the consent window correctly, which is what localised it -- the channel, the background listener, window creation and bounds are all fine. Every deliberate refusal in dispatchInbound already logs and returns; only a thrown error could vanish, and nothing was watching for one. The candidates on the task-consent path are enrolledExecutorDids() and parseTaskConsentRequest(), both awaited before any logging. Log it. This does not stop the throw -- the logged error is what will name the actual cause -- but a consent request can no longer disappear without trace after its mediator copy has been deleted. Signed-off-by: Glenn Gore --- packages/extension/src/offscreen.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/extension/src/offscreen.ts b/packages/extension/src/offscreen.ts index 6372359..13384f7 100644 --- a/packages/extension/src/offscreen.ts +++ b/packages/extension/src/offscreen.ts @@ -1766,6 +1766,24 @@ async function handleInbound( ): Promise { try { await dispatchInbound(conn, identity, signing, vtaDid, message, isApprover, fromDrain); + } catch (err) { + // There was no catch here, and the call site is `void handleInbound(...)`. + // So anything dispatch threw — rather than returned as a refusal — became an + // unhandled rejection: no log from this file, no prompt, no decision sent + // back to the executor, and the message already acked to the mediator so its + // queued copy was gone. + // + // That is indistinguishable from a request that never arrived, and it is + // what it looked like: `[pnm inbound] received` followed by silence, while + // the same request dispatched by hand from the console prompted correctly. + // Every deliberate refusal in `dispatchInbound` logs and returns; only a + // *thrown* error could vanish, and nothing was watching for one. + console.error( + "[pnm inbound] handling threw — no prompt was raised for this message:", + typeof message.type === "string" ? message.type : "(no type)", + typeof message.id === "string" ? message.id : "(no id)", + err, + ); } finally { const id = typeof message.id === "string" ? message.id : undefined; if (id) {