paperclipai/paperclip · error · Error
Invalid install payload identifier '${identifier}'.
Error message
Invalid install payload identifier '${identifier}'. What it means
Thrown by payloadPathFor() when the supplied identifier does not match the regex /^[A-Za-z0-9._-]+$/. Identifiers are joined into filesystem paths under installsRoot, so they must be safe single-segment names — no slashes, spaces, colons, or shell metacharacters — to prevent path traversal and traversal-adjacent issues.
Source
Thrown at cli/src/install-store.ts:210
return await callback();
} finally {
try {
if (fs.readFileSync(paths.lockPath, "utf8").trim() === token) {
fs.rmSync(paths.lockPath, { force: true });
}
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
}
}
}
export function payloadPathFor(
paths: InstallStorePaths,
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;View on GitHub (pinned to 67001ec6eb)
Solutions
- Sanitize the identifier to only contain A-Z, a-z, 0-9, '.', '_', '-' before calling payloadPathFor.
- Replace slashes in git refs/branches with a safe separator like '-' (e.g. 'feature-foo') before use.
- Use the version string directly (semver versions already match the regex).
- Reject empty identifiers upstream before reaching this call.
Example fix
// before payloadPathFor(paths, "git", "feature/auth"); // throws: '/' not allowed // after const safeId = "feature/auth".replace(/[^A-Za-z0-9._-]/g, "-"); // "feature-auth" payloadPathFor(paths, "git", safeId);
Defensive patterns
Strategy: validation
Validate before calling
function isValidPayloadIdentifier(id: string): boolean {
return typeof id === "string" && /^[A-Za-z0-9._-]+$/.test(id);
}
// Before calling payloadPathFor:
if (!isValidPayloadIdentifier(id)) throw new Error(`Bad payload id: ${id}`); Type guard
function isPayloadIdentifier(value: unknown): value is string {
return typeof value === "string" && /^[A-Za-z0-9._-]+$/.test(value);
} Try / catch
try {
payloadPathFor(paths, source, id);
} catch (err) {
if (err instanceof Error && err.message.startsWith("Invalid install payload identifier")) {
const safe = id.replace(/[^A-Za-z0-9._-]/g, "-");
payloadPathFor(paths, source, safe);
} else throw err;
} Prevention
- Sanitize git refs/branches (replace '/' with '-') before using as identifier.
- Prefer semver version strings, which already match the allowed character set.
- Reject empty identifiers upstream.
- Never derive identifiers from raw filesystem paths or URLs.
When it happens
Trigger: Called payloadPathFor(paths, source, identifier) with an identifier containing characters outside [A-Za-z0-9._-], e.g. a version like '1.2.3/beta', '1.2.3:rc1', a path with spaces, or an empty string.
Common situations: 1) Passing a git ref containing a slash (e.g. 'feature/foo') as the identifier. 2) Passing a version with a prerelease tag separator not in the allowed set (e.g. '1.0.0+build' is fine but '1.0.0 rc1' is not). 3) Passing a relative path like '../x' or an absolute path. 4) Empty or whitespace-only identifier.
Related errors
- Refusing to write export file outside output directory: ${re
- Refusing to remove install store with an invalid manifest at
- unsupported manifest shape
- Refusing to activate payload outside ${paths.installsRoot}.
- Refusing to activate payload that resolves outside ${paths.i
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/2bc006ece8f1da64.
Report an issue: GitHub.