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

  1. Sanitize the identifier to only contain A-Z, a-z, 0-9, '.', '_', '-' before calling payloadPathFor.
  2. Replace slashes in git refs/branches with a safe separator like '-' (e.g. 'feature-foo') before use.
  3. Use the version string directly (semver versions already match the regex).
  4. 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

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


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