Startup hangs for minutes on offline/airgapped hosts: plugin installs and startup fetches are unbounded · Issue #57742 · anomalyco/opencode · GitHub
Skip to content

Startup hangs for minutes on offline/airgapped hosts: plugin installs and startup fetches are unbounded #57742

Open

Description

@PJ-Zetier

Description

On hosts without internet where connections hang instead of failing fast (hanging DNS / black-holed TCP — typical airgapped or firewalled networks), opencode takes minutes to become usable, on every launch:

  • configured package plugins run a real Arborist install at startup with no opencode-side timeout. npm's default retry ladder (3 attempts + 10s/60s backoff) makes one cold plugin spec cost ~100-160s with hanging DNS and ~6-13 min with black-holed TCP — and both the TUI first frame (plugins.ready gate) and the server's plugin activation latch wait on it.
  • several awaited startup fetches have no timeout at all: the pre-renderer file.list call to the local server (frozen blank terminal if the server stalls), URL skill sources, remote instruction URLs, OAuth credential refresh, and the Console config fetch.

Measured on Linux in a network namespace that drops outbound SYNs and DNS (tcp_syn_retries=6 ≈ 127s per connect; resolver ~10s per lookup). Older reports of the same class: #2224, #19197.

Plugins

Any npm package spec in config plugins with a cold cache reproduces it; URL skill/instruction sources reproduce the smaller stalls.

OpenCode version

dev @ 92242b5 (plugin-install behavior also reproduced on the v2.1.1 release binary)

Steps to reproduce

  1. Add a plugin package spec that is not in the local cache (or a URL skill/instruction source) to config
  2. Make outbound traffic hang rather than refuse (e.g. a netns with a black-holed default route, or a firewall that drops instead of rejecting)
  3. Run opencode — usability stalls for one full npm retry cycle (~2-13 min depending on failure mode), every launch

Operating System

Ubuntu 22.04

Activity

  1. opencode-agent commented on Oct 21, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report. I reproduced it on v2.1.1, and it still happens on the latest dev (3a1f9a9).

    What I did:

    • Configured plugins: ["is-number"] with an empty cache.
    • Pointed the npm registry at a non-routable address (npm_config_registry=http://10.255.255.1/), so connections hang silently instead of failing.
    • Started the TUI and sent a prompt to a simulated model.

    Results:

    • With the hanging registry, the prompt got no response for the whole 63 seconds I waited, on both 2.1.1 and dev.
    • With no plugin, or with the normal registry, the response arrived in about 13 seconds.

    The prompt waits for plugin activation, and activation waits for the npm/Arborist install, which has no timeout of its own. The recording and the script below show this.

    One difference from your description: in my runs the TUI's first screen was not held at "Loading plugins…". It was usable after about 3 seconds, and the stall was on the first prompt. I didn't test the other startup fetches you listed (URL skills, remote instructions, OAuth refresh, Console config).

    Related: #56885 (TUI stuck on "Loading plugins…" because of a stalled opencode.ai connection) and your feature request #57743 (OPENCODE_OFFLINE).

    recording.mp4

    Screenshot

    Reproduction script (drive.ts)
    // Repro for #57742: a configured npm plugin with a cold cache, on a network that
    // black-holes traffic, blocks prompts (Plugin.awaitActivation) behind an npm/Arborist
    // install with no opencode-side timeout.
    // Run with the registry pointed at a non-routable address (SYNs are silently dropped):
    //   npm_config_registry=http://10.255.255.1/ OPENCODE_DRIVE_MEDIA_DIR=/workspace/media \
    //     opencode-drive run drive.ts
    import { Effect } from "effect"
    import { Llm, OpenCodeDriver } from "opencode-drive"
    
    const WAIT_SECONDS = Number(process.env.WAIT_SECONDS ?? 120)
    
    export default OpenCodeDriver.use(
      {
        ...(process.env.OC_DEV ? { opencode: { dev: process.env.OC_DEV } } : {}),
        tui: { recording: true },
        config: (process.env.NO_PLUGIN ? {} : { plugins: [(process.env.PLUGIN ?? "is-number")] }) as any,
      },
      ({ ui, llm }) =>
        Effect.gen(function* () {
          const start = Date.now()
          yield* Effect.sleep("3 seconds")
          yield* llm.queue(Llm.text("PONG from the simulated model"))
          yield* ui.submit("ping")
          yield* ui.screenshot("prompt-submitted")
          for (let i = 0; i < WAIT_SECONDS / 10; i++) {
            yield* Effect.sleep("10 seconds")
            const frame = yield* ui.capture()
            const text = frame.lines.map((l: any) => l.spans.map((s: any) => s.text).join("")).join("\n")
            const answered = text.includes("PONG from the simulated model")
            yield* Effect.log(`t=${Math.round((Date.now() - start) / 1000)}s answered: ${answered}`)
            if (answered) return
          }
          yield* ui.screenshot("still-waiting")
          yield* Effect.log(`NO RESPONSE after ${Math.round((Date.now() - start) / 1000)}s: prompt blocked on plugin install`)
        }),
    )

    Run it with opencode-drive: opencode-drive run ./drive.ts. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions