Skip to content

Commit 8ba48bd

Browse files
authored
Merge pull request #7049 from shin-core/fix/chat-dispatch-handler-catch-6989-v2
fix(miner): fail closed when a chat-action handler throws
2 parents cd9aedf + bdb11d9 commit 8ba48bd

2 files changed

Lines changed: 29 additions & 1 deletion

File tree

packages/loopover-miner/lib/chat-action-dispatch.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,17 @@ export async function dispatchChatAction(request, options = {}) {
7171
return { ok: false, status: "invalid_params", action };
7272
}
7373

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

test/unit/miner-chat-action-dispatch.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,22 @@ describe("dispatchChatAction (#6519)", () => {
147147
const result = await dispatchChatAction({ action: "demo", params: {} }, { env: enabledEnv, registry });
148148
expect(result).toEqual({ ok: false, status: "invalid_params", action: "demo", error: "boom" });
149149
});
150+
151+
it("fails closed with handler_error when the handler throws, not an unhandled rejection (#6989)", async () => {
152+
const registry = registryWith("demo", () => true, () => {
153+
throw new Error("network down");
154+
});
155+
const result = await dispatchChatAction({ action: "demo", params: {} }, { env: enabledEnv, registry });
156+
// Typed result, distinct from invalid_params so a caller can tell an execution failure from a validation
157+
// failure. The thrown value is deliberately not surfaced (it may carry external detail).
158+
expect(result).toEqual({ ok: false, status: "handler_error", action: "demo" });
159+
});
160+
161+
it("fails closed the same way regardless of what the handler throws (#6989)", async () => {
162+
const registry = registryWith("demo", () => true, () => {
163+
throw "kaboom";
164+
});
165+
const result = await dispatchChatAction({ action: "demo", params: {} }, { env: enabledEnv, registry });
166+
expect(result).toEqual({ ok: false, status: "handler_error", action: "demo" });
167+
});
150168
});

0 commit comments

Comments
 (0)