fix(httpapi): handle corrupt v2 session messages (#28633)

This commit is contained in:
Shoubhit Dash
2026-05-21 16:30:04 +05:30
committed by GitHub
parent 4a976482b1
commit 9739d75892
7 changed files with 120 additions and 13 deletions

View File

@@ -129,4 +129,17 @@ describe("PublicApi OpenAPI v2 errors", () => {
)
}
})
test("documents v2 session read data errors", () => {
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
for (const route of [
["get", "/api/session/{sessionID}/context"],
["get", "/api/session/{sessionID}/message"],
] as const) {
expect(componentName(responseRef(spec.paths[route[1]]?.[route[0]]?.responses?.["500"]) ?? "")).toMatch(
/^UnknownError\d*$/,
)
}
})
})

View File

@@ -139,6 +139,24 @@ const insertLegacyAssistantMessage = (sessionID: SessionIDType, time = 1) =>
)
})
const insertCorruptV2Message = (sessionID: SessionIDType, time = 1) =>
Effect.sync(() =>
Database.use((db) =>
db
.insert(SessionMessageTable)
.values([
{
id: SessionMessage.ID.create(),
session_id: sessionID,
type: "assistant",
time_created: time,
data: {} as NonNullable<(typeof SessionMessageTable.$inferInsert)["data"]>,
},
])
.run(),
),
)
const setLegacySummaryDiff = (sessionID: SessionIDType) =>
Effect.sync(() =>
Database.use((db) =>
@@ -481,6 +499,41 @@ describe("session HttpApi", () => {
{ git: true, config: { formatter: false, lsp: false } },
)
it.instance(
"returns safe v2 unknown errors for corrupt projected messages",
() =>
Effect.gen(function* () {
const test = yield* TestInstance
const session = yield* createSession({ title: "v2 corrupt message" })
yield* insertCorruptV2Message(session.id)
const messages = yield* request(`/api/session/${session.id}/message`, {
headers: { "x-opencode-directory": test.directory },
})
const messagesBody = yield* responseJson(messages)
expect(messages.status).toBe(500)
expect(messagesBody).toMatchObject({
_tag: "UnknownError",
message: "Unexpected server error. Check server logs for details.",
})
expect((messagesBody as { ref?: unknown }).ref).toMatch(/^err_[0-9a-f-]{8}$/)
expect(JSON.stringify(messagesBody)).not.toContain("assistant")
const context = yield* request(`/api/session/${session.id}/context`, {
headers: { "x-opencode-directory": test.directory },
})
const contextBody = yield* responseJson(context)
expect(context.status).toBe(500)
expect(contextBody).toMatchObject({
_tag: "UnknownError",
message: "Unexpected server error. Check server logs for details.",
})
expect((contextBody as { ref?: unknown }).ref).toMatch(/^err_[0-9a-f-]{8}$/)
expect(JSON.stringify(contextBody)).not.toContain("assistant")
}),
{ git: true, config: { formatter: false, lsp: false } },
)
it.instance(
"serves sessions with migrated summary diffs missing file details",
() =>