From fc08292136200551155558412b94d6ddb6772d41 Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Thu, 21 May 2026 15:44:35 +0530 Subject: [PATCH] fix(httpapi): add unknown error references (#28629) --- packages/core/src/util/error.ts | 1 + .../instance/httpapi/middleware/error.ts | 5 +++- .../server/httpapi-error-middleware.test.ts | 23 +++++++++---------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/core/src/util/error.ts b/packages/core/src/util/error.ts index 7338571f29..5fe41e1cff 100644 --- a/packages/core/src/util/error.ts +++ b/packages/core/src/util/error.ts @@ -65,5 +65,6 @@ export abstract class NamedError extends Error { public static readonly Unknown = NamedError.create("UnknownError", { message: Schema.String, + ref: Schema.optional(Schema.String), }) } diff --git a/packages/opencode/src/server/routes/instance/httpapi/middleware/error.ts b/packages/opencode/src/server/routes/instance/httpapi/middleware/error.ts index 7b5643fd68..bf4dacd67e 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/middleware/error.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/middleware/error.ts @@ -26,12 +26,15 @@ export const errorLayer = HttpRouter.middleware<{ handles: unknown }>()((effect) return Effect.succeed(HttpServerResponse.jsonUnsafe(error.toObject(), { status: 400 })) } - log.error("failed", { error, cause: Cause.pretty(cause) }) + const ref = `err_${crypto.randomUUID().slice(0, 8)}` + + log.error("failed", { ref, error, cause: Cause.pretty(cause) }) return Effect.succeed( HttpServerResponse.jsonUnsafe( new NamedError.Unknown({ message: "Unexpected server error. Check server logs for details.", + ref, }).toObject(), { status: 500 }, ), diff --git a/packages/opencode/test/server/httpapi-error-middleware.test.ts b/packages/opencode/test/server/httpapi-error-middleware.test.ts index 51d4cf9e0c..bd8e3d2deb 100644 --- a/packages/opencode/test/server/httpapi-error-middleware.test.ts +++ b/packages/opencode/test/server/httpapi-error-middleware.test.ts @@ -10,6 +10,14 @@ import { testEffect } from "../lib/effect" const it = testEffect(Layer.mergeAll(NodeHttpServer.layerTest, NodeServices.layer)) +function expectUnknownErrorBody(body: unknown) { + expect(body).toMatchObject({ + name: "UnknownError", + data: { message: "Unexpected server error. Check server logs for details." }, + }) + expect((body as { data?: { ref?: unknown } }).data?.ref).toMatch(/^err_[0-9a-f-]{8}$/) +} + describe("HttpApi error middleware", () => { it.live("returns a safe body for unknown 500 defects", () => Effect.gen(function* () { @@ -23,10 +31,7 @@ describe("HttpApi error middleware", () => { const body = yield* response.json expect(response.status).toBe(500) - expect(body).toEqual({ - name: "UnknownError", - data: { message: "Unexpected server error. Check server logs for details." }, - }) + expectUnknownErrorBody(body) expect(JSON.stringify(body)).not.toContain("secret stack marker") }), ) @@ -43,10 +48,7 @@ describe("HttpApi error middleware", () => { const body = yield* response.json expect(response.status).toBe(500) - expect(body).toEqual({ - name: "UnknownError", - data: { message: "Unexpected server error. Check server logs for details." }, - }) + expectUnknownErrorBody(body) expect(JSON.stringify(body)).not.toContain("secret named marker") }), ) @@ -84,10 +86,7 @@ describe("HttpApi error middleware", () => { const body = yield* response.json expect(response.status).toBe(500) - expect(body).toEqual({ - name: "UnknownError", - data: { message: "Unexpected server error. Check server logs for details." }, - }) + expectUnknownErrorBody(body) }), ) })