paperclipai/paperclip · error · Error

`pi --list-models` timed out.

Error message

`pi --list-models` timed out.

What it means

Thrown by discoverPiModels (pi-local) when the `pi --list-models` child process does not finish within 20s (timeoutSec=20, graceSec=3). The Pi adapter enumerates models via this CLI call, and a hang prevents any model discovery.

Source

Thrown at packages/adapters/pi-local/src/server/models.ts:127

  const cwd = asString(input.cwd, process.cwd());
  const env = normalizeEnv(input.env);
  const runtimeEnv = normalizeEnv({ ...process.env, ...env });

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

  if (result.timedOut) {
    throw new Error("`pi --list-models` timed out.");
  }
  if ((result.exitCode ?? 1) !== 0) {
    const detail = firstNonEmptyLine(result.stderr) || firstNonEmptyLine(result.stdout);
    throw new Error(detail ? `\`pi --list-models\` failed: ${detail}` : "`pi --list-models` failed.");
  }

  // Pi outputs model list to stderr, but fall back to stdout for older versions
  const output = result.stderr || result.stdout;
  return sortModels(dedupeModels(parseModelsOutput(output)));
}

function normalizeEnv(input: unknown): Record<string, string> {
  const envInput = typeof input === "object" && input !== null && !Array.isArray(input)
    ? (input as Record<string, unknown>)
    : {};
  const env: Record<string, string> = {};
  for (const [key, value] of Object.entries(envInput)) {
    if (typeof value === "string") env[key] = value;

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Run `pi --list-models` manually to identify what it is waiting on.
  2. Pre-authenticate the pi provider so enumeration does not block on auth.
  3. Verify the pi binary path (resolvePiCommand) points at a working install.
  4. If the hang is a known transient, retry discovery (the cache TTL will then hold the result).
Defensive patterns

Strategy: retry

Validate before calling

async function piResponsive(timeoutMs = 5000): Promise<boolean> {
  try {
    const r = await runChildProcess("pi-probe", "pi", ["--version"], { timeoutSec: timeoutMs/1000, graceSec: 1, onLog: async () => {} });
    return (r.exitCode ?? 1) === 0;
  } catch { return false; }
}

Try / catch

let attempt = 0;
while (true) {
  try {
    return await discoverPiModels(input);
  } catch (e) {
    if (e instanceof Error && /`pi --list-models` timed out/.test(e.message) && attempt++ < 1) continue;
    throw e;
  }
}

Prevention

When it happens

Trigger: runChildProcess for `pi --list-models` returns { timedOut: true }.

Common situations: pi binary slow to start (cold cache, antivirus); pi hanging on a provider network call; pi prompting interactively in a non-TTY; overloaded host.

Understand the failure class

Related errors


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