| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 |
- import { NamedError } from "@opencode-ai/core/util/error"
- import * as Log from "@opencode-ai/core/util/log"
- import { ConfigError } from "@/config/error"
- import { Cause, Effect } from "effect"
- import { HttpRouter, HttpServerError, HttpServerRespondable, HttpServerResponse } from "effect/unstable/http"
- import fs from "fs/promises"
- const log = Log.create({ service: "server" })
- const errorLogPath = "/tmp/opencode-http-errors.log"
- function writeHttpError(cause: Cause.Cause<unknown>, error: unknown) {
- void fs.appendFile(
- errorLogPath,
- JSON.stringify({
- time: new Date().toISOString(),
- error: error instanceof Error ? error.message : String(error),
- stack: error instanceof Error ? error.stack : undefined,
- cause: Cause.pretty(cause),
- }) + "\n",
- )
- }
- // 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.catchCause((cause) => {
- const defect = cause.reasons.filter(Cause.isDieReason).find((reason) => {
- if (HttpServerResponse.isHttpServerResponse(reason.defect)) return false
- if (HttpServerError.isHttpServerError(reason.defect)) return false
- if (HttpServerRespondable.isRespondable(reason.defect)) return false
- return true
- })
- if (!defect) return Effect.failCause(cause)
- const error = defect.defect
- if (
- error instanceof NamedError &&
- (ConfigError.InvalidError.isInstance(error) || ConfigError.JsonError.isInstance(error))
- ) {
- return Effect.succeed(HttpServerResponse.jsonUnsafe(error.toObject(), { status: 400 }))
- }
- log.error("failed", { error, cause: Cause.pretty(cause) })
- return Effect.succeed(
- HttpServerResponse.jsonUnsafe(
- new NamedError.Unknown({
- message: "Unexpected server error. Check server logs for details.",
- }).toObject(),
- { status: 500 },
- ),
- )
- }),
- ),
- ).layer
|