paperclipai/paperclip · error

Managed git install metadata is incomplete.

Error message

Managed git install metadata is incomplete.

What it means

Thrown by updateCommand when the install manifest's source is 'git' but one or more of repo, ref, or sha is missing/falsy. Git-sourced managed installs require all three fields to resolve the remote ref to a target commit and to detect when the ref is pinned (a hex ref). Without them the update flow cannot safely fetch or compare commits.

Source

Thrown at cli/src/commands/update.ts:185

export async function updateCommand(options: UpdateOptions, overrides: Partial<Dependencies> = {}): Promise<void> {
  const paths = overrides.paths ?? resolveInstallStorePaths();
  const executablePath = overrides.executablePath ?? process.argv[1] ?? "";
  const runCommand = overrides.runCommand ?? execFileAsync;
  const mode = detectInstallMode(executablePath, paths);
  const manifest = readInstallManifest(paths);
  if (options.rollback) {
    if (mode !== "managed") throw new Error("--rollback is only available for managed installs.");
    if (options.dryRun) { emit(options, { mode, action: "rollback", dryRun: true, target: manifest?.previous[0]?.version ?? null }, `Would roll back to ${manifest?.previous[0]?.version ?? "the previous payload"}.`); return; }
    const next = await withInstallStoreLock(async () => rollbackManagedInstall(paths), paths);
    const restarted = await (overrides.restartActiveService ?? restartActiveManagedService)(next.version);
    emit(options, { mode, action: "rollback", version: next.version, restarted }, pc.green(`Rolled back to paperclipai ${next.version}${restarted ? " and restarted the active service" : ""}. Database migrations are not reversed; restore the pre-update backup if needed.`));
    return;
  }
  if (mode === "npx") { emit(options, { mode, action: "install" }, "This is an ephemeral npx install. Run `paperclipai install`, then use `paperclipai update` from the managed shim."); return; }
  if (mode === "source" || mode === "unknown") { emit(options, { mode, action: "manual" }, "This appears to be a source checkout. Update it with `git pull` followed by `pnpm install`; Paperclip will not mutate the repository."); return; }
  const request = resolveUpdateRequest(mode === "managed" ? manifest : null, options);
  if (mode === "managed" && manifest?.source === "git") {
    if (!manifest.repo || !manifest.ref || !manifest.sha) throw new Error("Managed git install metadata is incomplete.");
    if (/^[0-9a-f]{7,40}$/i.test(manifest.ref)) { emit(options, { mode, source: "git", pinned: true, sha: manifest.sha }, `Git install is pinned at ${manifest.sha.slice(0, 12)}.`); return; }
    const targetSha = await resolveGitHubRef(manifest.repo, manifest.ref, runCommand);
    if (targetSha === manifest.sha) { emit(options, { mode, source: "git", changed: false, sha: targetSha, ref: manifest.ref }, `${manifest.repo}@${manifest.ref} is already at ${targetSha.slice(0, 12)}.`); return; }
    if (options.check || options.dryRun) { emit(options, { mode, source: "git", changed: true, currentSha: manifest.sha, targetSha, ref: manifest.ref, dryRun: Boolean(options.dryRun) }, `Git update available: ${manifest.sha.slice(0, 12)} → ${targetSha.slice(0, 12)}.`); if (options.check) process.exitCode = 10; return; }
    if (options.yes !== true) {
      const confirmed = await (overrides.confirm ?? defaultConfirm)(`Update from ${manifest.repo}@${manifest.ref} and execute build scripts from commit ${targetSha.slice(0, 12)}?`);
      if (!confirmed) throw new Error("Git update cancelled. Re-run with --yes to confirm executing build scripts from the updated commit.");
    }
    if (options.backup !== false) await runPreUpdateBackup(options, overrides.backup ?? (() => dbBackupCommand({})), overrides.hasInstanceData);
    const installed = await withInstallStoreLock(async () => {
      const payload = await installGitPayload(manifest.repo!, targetSha, runCommand, paths);
      const record: InstallRecord = { source: "git", version: payload.version, channel: "pinned", repo: manifest.repo, ref: manifest.ref, sha: targetSha, payloadPath: payload.payloadPath, installedAt: (overrides.now?.() ?? new Date()).toISOString() };
      const next = buildNextManifest(record, manifest); const oldTarget = fs.readlinkSync(paths.currentPath); flipCurrentAtomic(payload.payloadPath, paths);
      try { writeInstallManifestAtomic(next, paths); } catch (error) { flipCurrentAtomic(path.resolve(paths.cliRoot, oldTarget), paths); throw error; }
      pruneInstallPayloads(next, paths); return payload;
    }, paths);
    let restarted: boolean;
    try {

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Reinstall the managed CLI from git with `paperclipai install --git <repo> --ref <ref>` to repopulate repo/ref/sha.
  2. Edit the manifest.json directly to fill in the missing repo, ref, and sha values if you know them.
  3. Fall back to an npm-channel install: `paperclipai install` (no --git) to switch off the git source.
Defensive patterns

Strategy: validation

Validate before calling

import { readInstallManifest, resolveInstallStorePaths } from '../install-store.js';
function gitManifestComplete(): boolean {
  const m = readInstallManifest(resolveInstallStorePaths());
  return Boolean(m && m.source === 'git' && m.repo && m.ref && m.sha);
}
// if (!gitManifestComplete()) { /* reinstall from git to repopulate fields */ }

Type guard

function isCompleteGitManifest(m: unknown): m is { repo: string; ref: string; sha: string; source: 'git' } {
  if (typeof m !== 'object' || m === null) return false;
  const r = m as Record<string, unknown>;
  return r.source === 'git' && typeof r.repo === 'string' && r.repo.length > 0
    && typeof r.ref === 'string' && r.ref.length > 0
    && typeof r.sha === 'string' && r.sha.length > 0;
}

Prevention

When it happens

Trigger: Manifest source === 'git' and (manifest.repo, manifest.ref, or manifest.sha) is empty/null/undefined. Reached on the managed git branch of updateCommand (line 185). Can happen if the manifest was written by an older/buggy installer or hand-edited.

Common situations: Manifest corrupted or partially overwritten. Upgraded from an older CLI version that did not persist repo/ref/sha. Manual edits to manifest.json that dropped a field. Install recording interrupted mid-write.

Related errors


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