Skip to content

Commit 02ca1eb

Browse files
committed
hubErrorHandler: report unhandled route errors through reportError
The global onError handler only called log.error and returned a 500/422 body, so an unhandled route exception left no refId a user could quote back to support. It now reports through @corbits/error-sink's reportError (operation, tenantId when a tenant-scoped route set one, and path/method as extra) and includes the returned refId in both the generic 500 body and the guidance-error 422 body. Fixes CL-7132.
1 parent 6810af9 commit 02ca1eb

2 files changed

Lines changed: 26 additions & 11 deletions

File tree

‎apps/hub/src/hub-error-handler.ts‎

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
// workflow's launch body failing to read, a definition asset that never
66
// materialized — stays invisible until someone reports it from the UI.
77
// This handler is the one place every such exception is guaranteed to be
8-
// logged, and it maps a named consumer-facing error to a real 4xx rather
8+
// reported, and it maps a named consumer-facing error to a real 4xx rather
99
// than folding it into a generic message.
1010
import type { Context } from "hono";
11-
import { getLogger } from "@intx/log";
11+
import { reportError } from "@corbits/error-sink";
1212

1313
/**
1414
* Duck-typed rather than an `instanceof` allowlist: any error carrying a
@@ -28,24 +28,39 @@ function hasGuidance(err: unknown): err is Error & { guidance: string } {
2828
}
2929

3030
/**
31-
* Builds the handler passed to `app.onError`. Takes the logger as a
32-
* parameter (rather than constructing one internally) so a test can
33-
* inject a fake and assert on what got logged.
31+
* `app.onError` runs for routes mounted both inside and outside the
32+
* platform's tenant middleware, so `c`'s `Variables` aren't statically
33+
* known here; this narrows just enough to read `tenant.id` when a
34+
* tenant-scoped route set it, without claiming the wider type.
3435
*/
35-
export function hubErrorHandler(log: ReturnType<typeof getLogger>) {
36+
function extractTenantId(c: Context): string | undefined {
37+
return (c as unknown as Context<{ Variables: { tenant?: { id: string } } }>)
38+
.var.tenant?.id;
39+
}
40+
41+
/** Builds the handler passed to `app.onError`. */
42+
export function hubErrorHandler() {
3643
return (err: unknown, c: Context): Response | Promise<Response> => {
37-
const message = err instanceof Error ? err.message : String(err);
38-
log.error`Unhandled error on ${c.req.method} ${c.req.path}: ${message}`;
44+
const tenantId = extractTenantId(c);
45+
const refId = reportError(err, {
46+
operation: "hub.unhandled_route_error",
47+
...(tenantId !== undefined ? { tenantId } : {}),
48+
extra: { path: c.req.path, method: c.req.method },
49+
});
3950

4051
if (hasGuidance(err)) {
41-
return c.json({ error: { code: err.name, message: err.message } }, 422);
52+
return c.json(
53+
{ error: { code: err.name, message: err.message, refId } },
54+
422,
55+
);
4256
}
4357

4458
return c.json(
4559
{
4660
error: {
4761
code: "internal_error",
4862
message: "Something went wrong. Please try again.",
63+
refId,
4964
},
5065
},
5166
500,

‎apps/hub/src/index.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,8 +1100,8 @@ export async function createHub(config: HubConfig) {
11001100

11011101
// Without this, any exception escaping a route (extension or platform
11021102
// alike) falls through to Hono's built-in handler: a bare 500 with
1103-
// nothing logged. See `hubErrorHandler`'s own doc comment.
1104-
app.onError(hubErrorHandler(getLogger(["hub", "error"])));
1103+
// nothing reported. See `hubErrorHandler`'s own doc comment.
1104+
app.onError(hubErrorHandler());
11051105

11061106
// Extension routes mount under the tenant prefix, inside the
11071107
// platform's native tenant middleware, so every extension handler

0 commit comments

Comments
 (0)