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
- Inspect ~/.paperclip/cli/install.json and confirm schemaVersion, source, previous (array), and payloadPath (string) match the expected shape.
- If the manifest is from an incompatible newer version, upgrade the CLI to match, or remove the store and reinstall.
- If corrupt, remove cliRoot and reinitialize with 'paperclipai install'.
- 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
- Do not hand-edit install.json schema fields (schemaVersion, source, previous, payloadPath).
- When upgrading Paperclip, check whether INSTALL_MANIFEST_VERSION changed and follow migration notes.
- Restore install.json only from backups made by the same CLI version.
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
- Refusing to remove install store without a manifest at ${pat
- Refusing to remove install store with an invalid manifest at
- Invalid install payload identifier '${identifier}'.
- Could not read managed install manifest at ${paths.manifestP
- --payload must be a JSON object
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/11087ab9a21c2c15.
Report an issue: GitHub.