Skip to content

server: skill.list is empty on startup #51680

Closed

Description

@dbpolito

Description

The first SDK call to skills.list() can return an empty list before skill registration finishes. The SDK has no readiness signal, and an empty list is valid, so clients can’t reliably poll.

Plugins

None configured.

OpenCode version

v2.0.18

Steps to reproduce

  1. Start OpenCode with the default configuration.
  2. Call skills.list() through the SDK immediately.
  3. Observe an empty list; call again after startup and observe the registered skills.

Operating System

Darwin 25.6.0 (arm64)

Terminal

TERM=xterm-256color

Activity

  1. github-actions commented on Sep 27, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    • #51282: agent.list can return an incomplete plugin-registered list immediately after startup; it specifically suggests checking whether skill.list has the same behavior.
    • #35556: tracks the related initial plugin-activation race where list operations can observe an empty or partial registry.
  2. opencodejr commented on Oct 28, 2026

    @opencodejr

    Thanks for the report! I could reproduce this on v2.0.18, and it still happens on the latest dev (86ce19d).

    What I did:

    • Started opencode serve in a project that has one skill in .opencode/skills/demo/SKILL.md.
    • Polled GET /api/skill, which is the endpoint behind skills.list(), as soon as the server accepted requests.

    The first successful response was data: [] every time. The same call a few seconds later returned the built-in skills plus demo. The script below reproduces it.

    It looks like the agent, model and provider list endpoints wait for plugins to finish loading before they answer, but the skill list endpoint doesn't, so an early call sees an empty list. Adding that same wait to the skill handler fixed it in a local test. Related: #36117 added this wait for the model and provider reads.

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Reproduce #51680: GET /api/skill (SDK skills.list()) returns [] right after startup.
    # Usage: ./repro.sh              -> uses `opencode` from PATH
    #        OC_DEV=/path/to/checkout ./repro.sh  -> runs the CLI from source
    set -u
    export OPENCODE_SERVER_PASSWORD=repro
    PORT=${PORT:-47123}
    WORK=$(mktemp -d /tmp/opencode/skill-race.XXXX)
    export HOME=$WORK/home XDG_CONFIG_HOME=$WORK/home/.config XDG_DATA_HOME=$WORK/home/.local/share XDG_STATE_HOME=$WORK/home/.local/state XDG_CACHE_HOME=$WORK/home/.cache
    mkdir -p "$WORK/project/.opencode/skills/demo" "$HOME"
    cat > "$WORK/project/.opencode/skills/demo/SKILL.md" <<'EOF'
    ---
    name: demo
    description: Demo skill for the startup race reproduction
    ---
    Hello from the demo skill.
    EOF
    cd "$WORK/project" && git init -q
    
    if [ -n "${OC_DEV:-}" ]; then
      bun --conditions=browser --preload="$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js" \
        "$OC_DEV/packages/cli/src/index.ts" serve --hostname 127.0.0.1 --port $PORT >"$WORK/serve.log" 2>&1 &
    else
      opencode serve --hostname 127.0.0.1 --port $PORT >"$WORK/serve.log" 2>&1 &
    fi
    PID=$!
    trap 'kill $PID 2>/dev/null' EXIT
    
    URL="http://127.0.0.1:$PORT/api/skill?directory=$(python3 -c 'import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1]))' "$WORK/project")"
    first=""
    for i in $(seq 1 600); do
      body=$(curl -s -u opencode:repro --max-time 10 "$URL") && case "$body" in *'"data"'*) first=$body; break;; esac
      sleep 0.02
    done
    names() { python3 -c 'import json,sys;d=json.load(sys.stdin)["data"];print(len(d),"skills:",[x["id"] for x in d])'; }
    echo "FIRST successful skill.list: $(echo "$first" | names)"
    sleep 10
    echo "Same call 10s later:         $(curl -s -u opencode:repro "$URL" | names)"

    Run it: bash repro.sh

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions