Hmbown/CodeWhale · error · Error

Restore points require the runtime bearer token.

Error message

Restore points require the runtime bearer token.

What it means

stage_local_copy validates the source directory before copying anything into staging: resolve_manifest_path must find plugin.json, kimi.plugin.json, or plugin.toml directly at the source root. This also rejects symlinked roots/manifests. The error means the directory you passed simply has no supported manifest at its top level, so it is not recognizable as a plugin bundle.

Source

Thrown at extensions/vscode/src/runtime.ts:133

  if (response.statusCode === 401) {
    throw new Error("Thread summaries require the runtime bearer token.");
  }
  if (response.statusCode !== 200) {
    throw new Error(`Thread summary returned HTTP ${response.statusCode}.`);
  }

  return readThreadSummaries(response.body);
}

export async function listSnapshots(config: RuntimeConfig, limit = 8): Promise<SnapshotEntry[]> {
  const baseUrl = runtimeBaseUrl(config);
  const response = await requestJson(
    `${baseUrl}/v1/snapshots?limit=${encodeURIComponent(String(limit))}`,
    config.token,
  );

  if (response.statusCode === 401) {
    throw new Error("Restore points require the runtime bearer token.");
  }
  if (response.statusCode !== 200) {
    throw new Error(`Restore points returned HTTP ${response.statusCode}.`);
  }

  return readSnapshots(response.body);
}

export function startRuntimeTerminal(config: RuntimeConfig): vscode.Terminal {
  const terminal = vscode.window.createTerminal("CodeWhale Runtime");
  const args = [
    "serve",
    "--http",
    "--host",
    shellQuote(config.host),
    "--port",
    String(config.port),
  ];

View on GitHub (pinned to 8880682c63)

Solutions

  1. cd into the extracted/cloned tree and locate the directory that directly contains plugin.json (or kimi.plugin.json/plugin.toml); install that directory.
  2. If the archive added a wrapper folder, pass <extract-dir>/<wrapper>/ as the source.
  3. If you maintain the bundle, name the manifest one of the three supported files and place it at the bundle root.

Example fix

# before
plugin install ./my-plugin-main          # wrapper folder, no manifest at root

# after
plugin install ./my-plugin-main/my-plugin  # directory holding plugin.json
Defensive patterns

Strategy: validation

Validate before calling

if crate::plugins::agent_plugin::resolve_manifest_path(source).is_none() {
    anyhow::bail!("{} has no plugin.json/kimi.plugin.json/plugin.toml at its root — pass the bundle directory itself", source.display());
}
stage_local_copy(source, user_plugins_dir, max_size)?;

Type guard

fn is_plugin_bundle(dir: &Path) -> bool {
    crate::plugins::agent_plugin::resolve_manifest_path(dir).is_some()
}

Try / catch

match stage_local_copy(source, plugins_dir, max_size) {
    Ok(staged) => staged,
    Err(e) if e.to_string().contains("no plugin.json, kimi.plugin.json, or plugin.toml") => {
        // search one level down for the actual bundle root and retry
        let child = std::fs::read_dir(source)?.find_map(|d| {
            let d = d.ok()?.path();
            is_plugin_bundle(&d).then_some(d)
        }).context("no nested bundle found")?;
        stage_local_copy(&child, plugins_dir, max_size)?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Pointing the local-install API at a repository checkout root where the plugin lives in a subdirectory; pointing at the parent of the bundle; pointing at a file instead of a directory; a bundle shipping only an unsupported manifest filename.

Common situations: git clone of a plugin monorepo and installing the repo root instead of plugins/<name>; unzipping an archive that adds a wrapper folder (my-plugin-main/...) and passing the extraction dir; bundles that renamed their manifest (e.g. manifest.json) which is not in the supported set.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/68854f567422a110. Report an issue: GitHub.