From 3974520742e1f2a64209429b4e8c6c845f34f6b5 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 12 May 2026 14:09:00 -0400 Subject: [PATCH] Migrate UI cancel error to tagged error (#27112) --- packages/opencode/src/cli/error.ts | 4 ++-- packages/opencode/src/cli/ui.ts | 3 +-- packages/opencode/test/cli/error.test.ts | 5 +++++ packages/opencode/test/util/error.test.ts | 8 ++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/opencode/src/cli/error.ts b/packages/opencode/src/cli/error.ts index 628aa95696..6fd7b573e2 100644 --- a/packages/opencode/src/cli/error.ts +++ b/packages/opencode/src/cli/error.ts @@ -78,8 +78,8 @@ export function FormatError(input: unknown) { ].join("\n") } - // UICancelledError: void (no data) - if (NamedError.hasName(input, "UICancelledError")) { + // UICancelledError: user cancelled an interactive CLI prompt + if (isTaggedError(input, "UICancelledError") || NamedError.hasName(input, "UICancelledError")) { return "" } } diff --git a/packages/opencode/src/cli/ui.ts b/packages/opencode/src/cli/ui.ts index 69e04b925a..6ad6495cf1 100644 --- a/packages/opencode/src/cli/ui.ts +++ b/packages/opencode/src/cli/ui.ts @@ -1,5 +1,4 @@ import { EOL } from "os" -import { NamedError } from "@opencode-ai/core/util/error" import { Schema } from "effect" import { logo as glyphs } from "./logo" @@ -10,7 +9,7 @@ const wordmark = [ `▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`, ] -export const CancelledError = NamedError.create("UICancelledError", Schema.optional(Schema.Void)) +export class CancelledError extends Schema.TaggedErrorClass()("UICancelledError", {}) {} export const Style = { TEXT_HIGHLIGHT: "\x1b[96m", diff --git a/packages/opencode/test/cli/error.test.ts b/packages/opencode/test/cli/error.test.ts index 6af2633ce6..b4d1dbeda7 100644 --- a/packages/opencode/test/cli/error.test.ts +++ b/packages/opencode/test/cli/error.test.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from "bun:test" import { AccountTransportError } from "../../src/account/schema" import { FormatError } from "../../src/cli/error" +import { UI } from "../../src/cli/ui" describe("cli.error", () => { test("formats account transport errors clearly", () => { @@ -15,4 +16,8 @@ describe("cli.error", () => { expect(formatted).toContain("This failed before the server returned an HTTP response.") expect(formatted).toContain("Check your network, proxy, or VPN configuration and try again.") }) + + test("formats cancelled UI errors as empty output", () => { + expect(FormatError(new UI.CancelledError())).toBe("") + }) }) diff --git a/packages/opencode/test/util/error.test.ts b/packages/opencode/test/util/error.test.ts index 8d077b1f26..fdb559a231 100644 --- a/packages/opencode/test/util/error.test.ts +++ b/packages/opencode/test/util/error.test.ts @@ -1,8 +1,6 @@ import { describe, expect, test } from "bun:test" -import { Schema } from "effect" import { NamedError } from "@opencode-ai/core/util/error" import { errorData, errorFormat, errorMessage } from "../../src/util/error" -import { UI } from "../../src/cli/ui" import { MessageError } from "../../src/session/message-error" describe("util.error", () => { @@ -60,9 +58,7 @@ describe("util.error", () => { expect(error.toObject()).toEqual({ name: "ProviderAuthError", data: { providerID: "anthropic", message: "boom" } }) }) - test("void named errors accept JSON without data", () => { - const serialized = JSON.parse(JSON.stringify(new UI.CancelledError(undefined).toObject())) - - expect(Schema.decodeUnknownOption(UI.CancelledError.Schema)(serialized)._tag).toBe("Some") + test("named errors without fields serialize data", () => { + expect(new MessageError.OutputLengthError({}).toObject()).toEqual({ name: "MessageOutputLengthError", data: {} }) }) })