Skip to content

Windows: "Background service failed to start" after update until opencode service stop is run #62326

Open

Description

@leonxger

Description

Version: opencode v2.2.5 (updated from an earlier version, exact old version unknown; ~2-4 weeks old)
OS: Windows, PowerShell, Node via nvm4w

What happened
After updating, running opencode failed every time with:

Error: Background service failed to start
    at service.ensure (...)
    at cli.server-connection.resolve (...)
  • opencode serve --service exited silently with no output.
  • Adding --print-logs --log-level debug also printed nothing.
  • Get-Process showed one leftover opencode process (Id 51408) that I did not start.

Workaround

  1. opencode service stop (this removed the leftover process)
  2. opencode service start printed http://127.0.0.1:28894
  3. opencode then worked.

Expected
After an update, opencode should replace or restart the old background service, or at least report why it could not.

Suggestions

  • service.ensure should detect a service left over from an older version and replace it.
  • The error should include the child process's exit reason instead of only "failed to start".
  • serve --service should print something when it exits early.

Plugins

No response

OpenCode version

2.2.5

Steps to reproduce

No response

Screenshot

No response

Operating System

Windows 11

Terminal

No response

Activity

  1. opencodejr commented on Nov 6, 2026

    @opencodejr

    Thanks for the report and the workaround. I reproduced this on 2.2.5, and it still happens on the latest dev (0e655ff). I tested on Linux; the code involved is shared across platforms, so it isn't Windows-specific.

    Steps (the script below does all of this in a throwaway profile):

    1. Start the background service once, then stop it.
    2. Force the next start to fail while booting (I corrupted the service database). opencode service start fails with Background service failed to start, but the serve --service process keeps running.
    3. Restore a working database and start again. It still fails with the same error every time, opencode service status says stopped, and opencode serve --service --print-logs --log-level debug exits with no output.
    4. opencode service stop followed by opencode service start works again, just as you found.

    So the leftover process in your case was probably a service that failed to start (after your update) and never exited. OpenCode then refuses to start a new one while the stuck one is still registered. Your logs didn't show why that first start failed, so the original trigger may be something else.

    Related:

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Reproduces: a background service that failed to boot stays alive and every later
    # `opencode` / `opencode service start` fails with "Background service failed to start"
    # until `opencode service stop` is run.
    # Usage: OC="opencode" bash repro.sh   (or OC="bun ... packages/cli/src/index.ts")
    set -u
    OC=${OC:-opencode}
    T=$(mktemp -d /tmp/opencode/svc-XXXX)
    export HOME=$T/home XDG_CONFIG_HOME=$T/config XDG_DATA_HOME=$T/data XDG_STATE_HOME=$T/state XDG_CACHE_HOME=$T/cache
    mkdir -p $HOME $XDG_CONFIG_HOME $XDG_DATA_HOME $XDG_STATE_HOME $XDG_CACHE_HOME
    $OC --version
    $OC service set port 28894 >/dev/null 2>&1 || true
    echo "== 1. start once so the data dir exists"; $OC service start; $OC service stop; sleep 1
    DB=$(find $XDG_DATA_HOME -name 'opencode*.db' | head -1); echo "db: $DB"
    cp "$DB" "$T/db.bak"; rm -f "$DB"-wal "$DB"-shm
    echo "== 2. corrupt the db to force a failed boot"; head -c 4096 /dev/urandom > "$DB"
    timeout 60 $OC service start; echo "exit=$?"
    echo "processes:"; pgrep -af 'serve --service' | grep -v pgrep
    echo "== 3. restore a healthy db and try again"; cp "$T/db.bak" "$DB"
    timeout 60 $OC service start; echo "exit=$?"
    timeout 60 $OC service start; echo "exit=$?"
    echo "status:"; $OC service status
    echo "processes:"; pgrep -af 'serve --service' | grep -v pgrep
    echo "== 4. serve --service directly"; timeout 20 $OC serve --service --print-logs --log-level debug 2>&1 | tail -5; echo "exit=$?"
    echo "== 5. workaround: service stop, then start"; $OC service stop; timeout 60 $OC service start; echo "exit=$?"
    $OC service stop

    Run it: bash repro.sh

  2. frankxai commented on Nov 7, 2026

    @frankxai

    Picked this up. The stuck state is ensure() treating any registered 500 from /api/info as fatal, so a boot that fails after registering blocks every later start. #62520 separates the two cases: a failure this call starts is still reported once and not respawned; a failure already registered before the call starts a contender is terminated and replaced, so the next start recovers once the boot cause is gone. Fixture tests cover both, for the Effect and Promise clients.

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