fix(server): return structured validation errors (#26457)

This commit is contained in:
Kit Langton
2026-05-09 00:21:19 -04:00
committed by GitHub
parent dc978cb889
commit ebe6087e8f
2 changed files with 50 additions and 2 deletions

View File

@@ -6,13 +6,41 @@ import { NamedError } from "@opencode-ai/core/util/error"
import * as Log from "@opencode-ai/core/util/log"
import { Cause, Effect } from "effect"
import { HttpRouter, HttpServerError, HttpServerRespondable, HttpServerResponse } from "effect/unstable/http"
import { HttpApiError } from "effect/unstable/httpapi"
import { HttpApiSchemaError } from "effect/unstable/httpapi/HttpApiError"
const log = Log.create({ service: "server" })
function badRequestResponse() {
return HttpServerResponse.jsonUnsafe(
{
data: {},
errors: [],
success: false,
},
{ status: 400 },
)
}
function normalizeEmptyBadRequest(response: HttpServerResponse.HttpServerResponse) {
if (response.status !== 400 || response.body._tag !== "Empty") return response
return badRequestResponse()
}
// Keep typed HttpApi failures on their declared error path; this boundary only replaces defect-only empty 500s.
export const errorLayer = HttpRouter.middleware<{ handles: unknown }>()((effect) =>
effect.pipe(
Effect.catch((error) => {
if (error instanceof HttpApiError.BadRequest) return Effect.succeed(badRequestResponse())
return Effect.fail(error)
}),
Effect.map(normalizeEmptyBadRequest),
Effect.catchCause((cause) => {
const schemaError = cause.reasons
.filter(Cause.isDieReason)
.find((reason) => HttpApiSchemaError.is(reason.defect))
if (schemaError) return Effect.succeed(badRequestResponse())
const defect = cause.reasons.filter(Cause.isDieReason).find((reason) => {
if (HttpServerResponse.isHttpServerResponse(reason.defect)) return false
if (HttpServerError.isHttpServerError(reason.defect)) return false
@@ -35,7 +63,7 @@ export const errorLayer = HttpRouter.middleware<{ handles: unknown }>()((effect)
return 500
}),
}),
)
)
}
if (error instanceof Session.BusyError) {
return Effect.succeed(