From eadc3eaf42b676962852f96643641c88648e4cbf Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 30 Aug 2026 13:48:58 +0000 Subject: [PATCH] fix: CLI commands web and serve don't show a useful message when port is used --- packages/opencode/src/cli/cmd/serve.ts | 10 +++++++--- packages/opencode/src/cli/cmd/web.ts | 10 +++++++--- packages/opencode/src/cli/network.ts | 5 +++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/cli/cmd/serve.ts b/packages/opencode/src/cli/cmd/serve.ts index c0f62b3ca071..2c8f8b083ff4 100644 --- a/packages/opencode/src/cli/cmd/serve.ts +++ b/packages/opencode/src/cli/cmd/serve.ts @@ -1,6 +1,6 @@ import { Effect } from "effect" -import { effectCmd } from "../effect-cmd" -import { withNetworkOptions, resolveNetworkOptions } from "../network" +import { effectCmd, fail } from "../effect-cmd" +import { isPortInUseError, withNetworkOptions, resolveNetworkOptions } from "../network" import { Flag } from "@opencode-ai/core/flag/flag" export const ServeCommand = effectCmd({ @@ -16,7 +16,11 @@ export const ServeCommand = effectCmd({ console.log("Warning: OPENCODE_SERVER_PASSWORD is not set; server is unsecured.") } const opts = yield* resolveNetworkOptions(args) - const server = yield* Effect.promise(() => Server.listen(opts)) + const server = yield* Effect.promise(() => Server.listen(opts)).pipe( + Effect.catchDefect((error) => + isPortInUseError(error) ? fail(`Port ${opts.port} is already in use`) : Effect.die(error), + ), + ) console.log(`opencode server listening on http://${server.hostname}:${server.port}`) yield* Effect.never diff --git a/packages/opencode/src/cli/cmd/web.ts b/packages/opencode/src/cli/cmd/web.ts index 69a981aada49..4d213b74f576 100644 --- a/packages/opencode/src/cli/cmd/web.ts +++ b/packages/opencode/src/cli/cmd/web.ts @@ -1,7 +1,7 @@ import { Effect } from "effect" import { UI } from "../ui" -import { effectCmd } from "../effect-cmd" -import { withNetworkOptions, resolveNetworkOptions } from "../network" +import { effectCmd, fail } from "../effect-cmd" +import { isPortInUseError, resolveNetworkOptions, withNetworkOptions } from "../network" import { Flag } from "@opencode-ai/core/flag/flag" import open from "open" import { networkInterfaces } from "os" @@ -41,7 +41,11 @@ export const WebCommand = effectCmd({ UI.println(UI.Style.TEXT_WARNING_BOLD + "! OPENCODE_SERVER_PASSWORD is not set; server is unsecured.") } const opts = yield* resolveNetworkOptions(args) - const server = yield* Effect.promise(() => Server.listen(opts)) + const server = yield* Effect.promise(() => Server.listen(opts)).pipe( + Effect.catchDefect((error) => + isPortInUseError(error) ? fail(`Port ${opts.port} is already in use`) : Effect.die(error), + ), + ) UI.empty() UI.println(UI.logo(" ")) UI.empty() diff --git a/packages/opencode/src/cli/network.ts b/packages/opencode/src/cli/network.ts index 22040ed41015..71dcc3c10972 100644 --- a/packages/opencode/src/cli/network.ts +++ b/packages/opencode/src/cli/network.ts @@ -1,6 +1,7 @@ import type { Argv, InferredOptionTypes } from "yargs" import { ConfigV1 } from "@opencode-ai/core/v1/config/config" import type { Config } from "@/config/config" +import { isRecord } from "@/util/record" import { Effect } from "effect" const options = { @@ -78,3 +79,7 @@ export function resolveNetworkOptionsNoConfig(args: NetworkOptions, config?: Con return { hostname, port, mdns, mdnsDomain, cors } } + +export function isPortInUseError(error: unknown) { + return isRecord(error) && isRecord(error.cause) && error.cause.code === "EADDRINUSE" +}