error.ts 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  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. import fs from "fs/promises"
  7. const log = Log.create({ service: "server" })
  8. const errorLogPath = "/tmp/opencode-http-errors.log"
  9. function writeHttpError(cause: Cause.Cause<unknown>, error: unknown) {
  10. void fs.appendFile(
  11. errorLogPath,
  12. JSON.stringify({
  13. time: new Date().toISOString(),
  14. error: error instanceof Error ? error.message : String(error),
  15. stack: error instanceof Error ? error.stack : undefined,
  16. cause: Cause.pretty(cause),
  17. }) + "\n",
  18. )
  19. }
  20. // Keep typed HttpApi failures on their declared error path; this boundary only replaces defect-only empty 500s.
  21. export const errorLayer = HttpRouter.middleware<{ handles: unknown }>()((effect) =>
  22. effect.pipe(
  23. Effect.catchCause((cause) => {
  24. const defect = cause.reasons.filter(Cause.isDieReason).find((reason) => {
  25. if (HttpServerResponse.isHttpServerResponse(reason.defect)) return false
  26. if (HttpServerError.isHttpServerError(reason.defect)) return false
  27. if (HttpServerRespondable.isRespondable(reason.defect)) return false
  28. return true
  29. })
  30. if (!defect) return Effect.failCause(cause)
  31. const error = defect.defect
  32. if (
  33. error instanceof NamedError &&
  34. (ConfigError.InvalidError.isInstance(error) || ConfigError.JsonError.isInstance(error))
  35. ) {
  36. return Effect.succeed(HttpServerResponse.jsonUnsafe(error.toObject(), { status: 400 }))
  37. }
  38. log.error("failed", { error, cause: Cause.pretty(cause) })
  39. return Effect.succeed(
  40. HttpServerResponse.jsonUnsafe(
  41. new NamedError.Unknown({
  42. message: "Unexpected server error. Check server logs for details.",
  43. }).toObject(),
  44. { status: 500 },
  45. ),
  46. )
  47. }),
  48. ),
  49. ).layer