Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/tui/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,11 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
const location = yield* Effect.tryPromise(() => api.file.list({ location: { directory: process.cwd() } })).pipe(
Effect.map((response) => response.location),
Effect.catch(() => Effect.tryPromise(() => api.location.get())),
// A server that is electing or cold-booting after an update can be unreachable for longer
// than both probes; exit through the CLI error channel instead of escaping as a raw defect.
Effect.catch((error) => Effect.succeed({ error })),
)
if ("error" in location) return { epilogue: undefined, reason: location.error }
const directory = location.directory
const pluginDirectories = yield* Effect.promise(() => localPluginDirectories(process.cwd(), global.config))
const handoff = input.terminalHandoff ? yield* Effect.promise(input.terminalHandoff) : undefined
Expand Down
6 changes: 6 additions & 0 deletions packages/tui/src/util/error.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ClientError } from "@opencode-ai/client"
import { isRecord } from "./record"

type ConfigIssue = { message: string; path: string[] }
Expand All @@ -8,6 +9,11 @@ export function cliErrorMessage(input: unknown): string | undefined {
if (formatted) return formatted
}

if (input instanceof ClientError) {
process.exitCode = 1
const detail = input.cause instanceof Error ? `: ${input.cause.message}` : ""
return `Could not reach the OpenCode server${detail}. If it was updating or restarting, wait a moment and try again.`
}
if (tagged(input, "CliError")) {
if (typeof input.exitCode === "number") process.exitCode = input.exitCode
return field(input, "message") ?? ""
Expand Down
21 changes: 21 additions & 0 deletions packages/tui/test/error-message.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expect, test } from "bun:test"
import { ClientError } from "@opencode-ai/client"
import { cliErrorMessage } from "../src/util/error"

test("client transport errors format as a friendly message", () => {
const error = new ClientError("Transport", {
cause: new Error("Unable to connect. Is the computer able to access the url?"),
})
try {
const message = cliErrorMessage(error)
expect(message).toContain("Could not reach the OpenCode server")
expect(message).toContain("Unable to connect. Is the computer able to access the url?")
expect(process.exitCode).toBe(1)
} finally {
process.exitCode = 0
}
})

test("unknown errors are left to the fallback formatter", () => {
expect(cliErrorMessage(new Error("boom"))).toBeUndefined()
})
Loading