Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion packages/loopover-miner/lib/chat-action-dispatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,17 @@ export async function dispatchChatAction(request, options = {}) {
return { ok: false, status: "invalid_params", action };
}

const result = await registered.handler(request);
let result;
try {
result = await registered.handler(request);
} catch {
// A handler that throws fails closed with the module's typed result shape (#6989), consistent with the
// paramsValidator catch above. The thrown value is deliberately NOT echoed back: a handler wraps
// arbitrary action work (e.g. a network call), so its error could carry external detail -- the sibling
// fail-closed paths (sentry.js, pretooluse-hook.js) likewise swallow rather than surface it. A distinct
// "handler_error" status still lets a caller tell an execution failure from a params-validation failure.
return { ok: false, status: "handler_error", action };
}
return { ok: true, status: "dispatched", action, result };
}

Expand Down
18 changes: 18 additions & 0 deletions test/unit/miner-chat-action-dispatch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,22 @@ describe("dispatchChatAction (#6519)", () => {
const result = await dispatchChatAction({ action: "demo", params: {} }, { env: enabledEnv, registry });
expect(result).toEqual({ ok: false, status: "invalid_params", action: "demo", error: "boom" });
});

it("fails closed with handler_error when the handler throws, not an unhandled rejection (#6989)", async () => {
const registry = registryWith("demo", () => true, () => {
throw new Error("network down");
});
const result = await dispatchChatAction({ action: "demo", params: {} }, { env: enabledEnv, registry });
// Typed result, distinct from invalid_params so a caller can tell an execution failure from a validation
// failure. The thrown value is deliberately not surfaced (it may carry external detail).
expect(result).toEqual({ ok: false, status: "handler_error", action: "demo" });
});

it("fails closed the same way regardless of what the handler throws (#6989)", async () => {
const registry = registryWith("demo", () => true, () => {
throw "kaboom";
});
const result = await dispatchChatAction({ action: "demo", params: {} }, { env: enabledEnv, registry });
expect(result).toEqual({ ok: false, status: "handler_error", action: "demo" });
});
});