error.ts 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041
  1. import { NamedError } from "@opencode-ai/core/util/error"
  2. import * as Log from "@opencode-ai/core/util/log"
  3. import { ConfigError } from "@/config/error"
  4. import { Cause, Effect } from "effect"
  5. import { HttpRouter, HttpServerError, HttpServerRespondable, HttpServerResponse } from "effect/unstable/http"
  6. const log = Log.create({ service: "server" })
  7. // Keep typed HttpApi failures on their declared error path; this boundary only replaces defect-only empty 500s.
  8. export const errorLayer = HttpRouter.middleware<{ handles: unknown }>()((effect) =>
  9. effect.pipe(
  10. Effect.catchCause((cause) => {
  11. const defect = cause.reasons.filter(Cause.isDieReason).find((reason) => {
  12. if (HttpServerResponse.isHttpServerResponse(reason.defect)) return false
  13. if (HttpServerError.isHttpServerError(reason.defect)) return false
  14. if (HttpServerRespondable.isRespondable(reason.defect)) return false
  15. return true
  16. })
  17. if (!defect) return Effect.failCause(cause)
  18. const error = defect.defect
  19. if (
  20. error instanceof NamedError &&
  21. (ConfigError.InvalidError.isInstance(error) || ConfigError.JsonError.isInstance(error))
  22. ) {
  23. return Effect.succeed(HttpServerResponse.jsonUnsafe(error.toObject(), { status: 400 }))
  24. }
  25. log.error("failed", { error, cause: Cause.pretty(cause) })
  26. return Effect.succeed(
  27. HttpServerResponse.jsonUnsafe(
  28. new NamedError.Unknown({
  29. message: "Unexpected server error. Check server logs for details.",
  30. }).toObject(),
  31. { status: 500 },
  32. ),
  33. )
  34. }),
  35. ),
  36. ).layer