paperclipai/paperclip · error · Error

unsupported manifest shape

Error message

unsupported manifest shape

What it means

Thrown internally by readInstallManifest() when the manifest JSON parses successfully but fails a structural shape check: schemaVersion must equal INSTALL_MANIFEST_VERSION (1), source must be 'npm' or 'git', previous must be an array, and payloadPath must be a string. If any of these fail, the inner throw produces this bare message. Note: this error is caught and re-wrapped by the outer catch into error 277's message, so callers normally see 277, not this raw text.

Source

Thrown at cli/src/install-store.ts:224

  source: InstallSource,
  identifier: string,
): string {
  if (!/^[A-Za-z0-9._-]+$/.test(identifier)) {
    throw new Error(`Invalid install payload identifier '${identifier}'.`);
  }
  return path.join(paths.installsRoot, source, identifier);
}

export function readInstallManifest(paths = resolveInstallStorePaths()): InstallManifest | null {
  try {
    const value = JSON.parse(fs.readFileSync(paths.manifestPath, "utf8")) as InstallManifest;
    if (
      value.schemaVersion !== INSTALL_MANIFEST_VERSION ||
      (value.source !== "npm" && value.source !== "git") ||
      !Array.isArray(value.previous) ||
      typeof value.payloadPath !== "string"
    ) {
      throw new Error("unsupported manifest shape");
    }
    return value;
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
    throw new Error(`Could not read managed install manifest at ${paths.manifestPath}: ${String(error)}`);
  }
}

export function writeInstallManifestAtomic(
  manifest: InstallManifest,
  paths = resolveInstallStorePaths(),
): void {
  ensurePrivateDirectory(paths.cliRoot);
  const temporaryPath = `${paths.manifestPath}.tmp-${process.pid}-${Date.now()}`;
  try {
    fs.writeFileSync(temporaryPath, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 });
    fs.renameSync(temporaryPath, paths.manifestPath);
  } finally {

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Inspect ~/.paperclip/cli/install.json and confirm schemaVersion, source, previous (array), and payloadPath (string) match the expected shape.
  2. If the manifest is from an incompatible newer version, upgrade the CLI to match, or remove the store and reinstall.
  3. If corrupt, remove cliRoot and reinitialize with 'paperclipai install'.
  4. Restore install.json from a backup if one exists.

Example fix

// before: install.json has "schemaVersion": 2 with CLI expecting 1
$ rm -rf ~/.paperclip/cli
$ paperclipai install   # writes manifest with schemaVersion 1
Defensive patterns

Strategy: validation

Validate before calling

import fs from "node:fs";
import { INSTALL_MANIFEST_VERSION, resolveInstallStorePaths } from "./install-store.js";

function manifestShapeIsValid(paths = resolveInstallStorePaths()): boolean {
  try {
    const v = JSON.parse(fs.readFileSync(paths.manifestPath, "utf8"));
    return v.schemaVersion === INSTALL_MANIFEST_VERSION
      && (v.source === "npm" || v.source === "git")
      && Array.isArray(v.previous)
      && typeof v.payloadPath === "string";
  } catch { return false; }
}

Type guard

import { INSTALL_MANIFEST_VERSION, type InstallManifest } from "./install-store.js";

function isInstallManifest(value: unknown): value is InstallManifest {
  const v = value as Record<string, unknown>;
  return typeof v === "object" && v !== null
    && v.schemaVersion === INSTALL_MANIFEST_VERSION
    && (v.source === "npm" || v.source === "git")
    && Array.isArray(v.previous)
    && typeof v.payloadPath === "string";
}

Try / catch

import { readInstallManifest } from "./install-store.js";

try {
  const m = readInstallManifest(paths);
} catch (err) {
  if (err instanceof Error && err.message.includes("Could not read managed install manifest")) {
    // inner cause may be 'unsupported manifest shape' (this error) — reset store
    console.error("Manifest shape invalid. Reset: rm -rf ~/.paperclip/cli && paperclipai install");
  }
  throw err;
}

Prevention

When it happens

Trigger: Called readInstallManifest() where install.json exists, parses as JSON, but has a wrong/missing schemaVersion, a source value other than 'npm'/'git', a non-array previous field, or a non-string payloadPath.

Common situations: 1) A future Paperclip version bumps INSTALL_MANIFEST_VERSION and an older CLI reads the newer manifest. 2) install.json was hand-edited or partially overwritten. 3) A different tool wrote a file at the manifest path. 4) A migration step failed to update schemaVersion.

Related errors


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