paperclipai/paperclip · error · Error

`opencode models` failed: ${detail}

Error message

`opencode models` failed: ${detail}

What it means

Thrown by discoverOpenCodeModels (LOCAL) when `opencode models` exits non-zero. The detail is the first non-empty line of stderr then stdout, surfacing opencode's own diagnostic. Same shape as the remote failure (389) but for local discovery.

Source

Thrown at packages/adapters/opencode-local/src/server/models.ts:153

  const result = await runChildProcess(
    `opencode-models-${Date.now()}-${Math.random().toString(16).slice(2)}`,
    command,
    ["models"],
    {
      cwd,
      env: runtimeEnv,
      timeoutSec: MODELS_DISCOVERY_TIMEOUT_MS / 1000,
      graceSec: 3,
      onLog: async () => {},
    },
  );

  if (result.timedOut) {
    throw new Error(`\`opencode models\` timed out after ${MODELS_DISCOVERY_TIMEOUT_MS / 1000}s.`);
  }
  if ((result.exitCode ?? 1) !== 0) {
    const detail = firstNonEmptyLine(result.stderr) || firstNonEmptyLine(result.stdout);
    throw new Error(detail ? `\`opencode models\` failed: ${detail}` : "`opencode models` failed.");
  }

  return sortModels(parseOpenCodeModelsOutput(result.stdout));
}

export async function discoverOpenCodeModelsCached(input: {
  command?: unknown;
  cwd?: unknown;
  env?: unknown;
} = {}): Promise<AdapterModel[]> {
  const command = resolveOpenCodeCommand(input.command);
  const cwd = asString(input.cwd, process.cwd());
  const env = normalizeEnv(input.env);
  const key = discoveryCacheKey(command, cwd, env);
  const now = Date.now();
  pruneExpiredDiscoveryCache(now);
  const cached = discoveryCache.get(key);
  if (cached && cached.expiresAt > now) return cached.models;

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Read the detail line — it carries opencode's error (e.g. 'command not found', auth error).
  2. Install opencode or point PAPERCLIP_OPENCODE_COMMAND at the binary.
  3. Authenticate the provider on the host (opencode login or API key env).
  4. Run `opencode models` in the same shell/env to reproduce and fix.
Defensive patterns

Strategy: try-catch

Validate before calling

async function openCodeInstalled(): Promise<boolean> {
  try { await runChildProcess("probe", "opencode", ["--version"], { timeoutSec: 5, graceSec: 1, onLog: async () => {} }); return true; } catch { return false; }
}

Try / catch

try {
  return await discoverOpenCodeModels(input);
} catch (e) {
  if (e instanceof Error && /`opencode models` failed/.test(e.message)) {
    // surface the detail to the operator; check PATH/PAPERCLIP_OPENCODE_COMMAND and provider auth; non-retryable config issue
  }
  throw e;
}

Prevention

When it happens

Trigger: runChildProcess returns exitCode != 0 (not timedOut) for local `opencode models`; detail = firstNonEmptyLine(stderr) || firstNonEmptyLine(stdout).

Common situations: opencode not installed / not on PATH on the host (override with PAPERCLIP_OPENCODE_COMMAND); provider auth missing; corrupt opencode config; version incompatibility.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/0a85ee532f5ece50. Report an issue: GitHub.