{"record":{"id":"e45798b2de97249d","repo":"paperclipai/paperclip","slug":"managed-git-install-metadata-is-incomplete","errorCode":null,"errorMessage":"Managed git install metadata is incomplete.","messagePattern":"Managed git install metadata is incomplete\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/src/commands/update.ts","lineNumber":185,"sourceCode":"export async function updateCommand(options: UpdateOptions, overrides: Partial<Dependencies> = {}): Promise<void> {\n  const paths = overrides.paths ?? resolveInstallStorePaths();\n  const executablePath = overrides.executablePath ?? process.argv[1] ?? \"\";\n  const runCommand = overrides.runCommand ?? execFileAsync;\n  const mode = detectInstallMode(executablePath, paths);\n  const manifest = readInstallManifest(paths);\n  if (options.rollback) {\n    if (mode !== \"managed\") throw new Error(\"--rollback is only available for managed installs.\");\n    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; }\n    const next = await withInstallStoreLock(async () => rollbackManagedInstall(paths), paths);\n    const restarted = await (overrides.restartActiveService ?? restartActiveManagedService)(next.version);\n    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.`));\n    return;\n  }\n  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; }\n  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; }\n  const request = resolveUpdateRequest(mode === \"managed\" ? manifest : null, options);\n  if (mode === \"managed\" && manifest?.source === \"git\") {\n    if (!manifest.repo || !manifest.ref || !manifest.sha) throw new Error(\"Managed git install metadata is incomplete.\");\n    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; }\n    const targetSha = await resolveGitHubRef(manifest.repo, manifest.ref, runCommand);\n    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; }\n    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; }\n    if (options.yes !== true) {\n      const confirmed = await (overrides.confirm ?? defaultConfirm)(`Update from ${manifest.repo}@${manifest.ref} and execute build scripts from commit ${targetSha.slice(0, 12)}?`);\n      if (!confirmed) throw new Error(\"Git update cancelled. Re-run with --yes to confirm executing build scripts from the updated commit.\");\n    }\n    if (options.backup !== false) await runPreUpdateBackup(options, overrides.backup ?? (() => dbBackupCommand({})), overrides.hasInstanceData);\n    const installed = await withInstallStoreLock(async () => {\n      const payload = await installGitPayload(manifest.repo!, targetSha, runCommand, paths);\n      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() };\n      const next = buildNextManifest(record, manifest); const oldTarget = fs.readlinkSync(paths.currentPath); flipCurrentAtomic(payload.payloadPath, paths);\n      try { writeInstallManifestAtomic(next, paths); } catch (error) { flipCurrentAtomic(path.resolve(paths.cliRoot, oldTarget), paths); throw error; }\n      pruneInstallPayloads(next, paths); return payload;\n    }, paths);\n    let restarted: boolean;\n    try {","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/commands/update.ts#L167-L203","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Reinstall the managed CLI from git with `paperclipai install --git <repo> --ref <ref>` to repopulate repo/ref/sha.","Edit the manifest.json directly to fill in the missing repo, ref, and sha values if you know them.","Fall back to an npm-channel install: `paperclipai install` (no --git) to switch off the git source."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import { readInstallManifest, resolveInstallStorePaths } from '../install-store.js';\nfunction gitManifestComplete(): boolean {\n  const m = readInstallManifest(resolveInstallStorePaths());\n  return Boolean(m && m.source === 'git' && m.repo && m.ref && m.sha);\n}\n// if (!gitManifestComplete()) { /* reinstall from git to repopulate fields */ }","typeGuard":"function isCompleteGitManifest(m: unknown): m is { repo: string; ref: string; sha: string; source: 'git' } {\n  if (typeof m !== 'object' || m === null) return false;\n  const r = m as Record<string, unknown>;\n  return r.source === 'git' && typeof r.repo === 'string' && r.repo.length > 0\n    && typeof r.ref === 'string' && r.ref.length > 0\n    && typeof r.sha === 'string' && r.sha.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Do not hand-edit manifest.json — let the installer record all git fields.","If migrating stores, copy manifest.json verbatim so repo/ref/sha survive.","Re-run `paperclipai install --git <repo> --ref <ref>` to refresh metadata after any corruption."],"tags":["git","managed-install","metadata","update"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}