{"record":{"id":"2c77a31a36ae117d","repo":"paperclipai/paperclip","slug":"managed-install-metadata-is-missing","errorCode":null,"errorMessage":"Managed install metadata is missing.","messagePattern":"Managed install metadata is missing\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/src/commands/update.ts","lineNumber":244,"sourceCode":"      try {\n        fs.writeFileSync(npmUserConfigPath, `registry=${PUBLIC_NPM_REGISTRY}\\n@paperclipai:registry=${PUBLIC_NPM_REGISTRY}\\n`, { mode: 0o600 });\n        await runCommand(\"npm\", args, {\n          env: {\n            ...process.env,\n            npm_config_registry: PUBLIC_NPM_REGISTRY,\n            NPM_CONFIG_REGISTRY: PUBLIC_NPM_REGISTRY,\n            npm_config_userconfig: npmUserConfigPath,\n            NPM_CONFIG_USERCONFIG: npmUserConfigPath,\n          },\n          maxBuffer: 16 * 1024 * 1024,\n        });\n      } finally {\n        fs.rmSync(npmConfigDir, { recursive: true, force: true });\n      }\n    }\n    emit(options, { mode, action: \"update\", targetVersion, dryRun: Boolean(options.dryRun), command: [\"npm\", ...args] }, options.dryRun ? \"Dry run complete.\" : pc.green(`Updated global npm install to ${targetVersion}.`)); return;\n  }\n  if (!manifest) throw new Error(\"Managed install metadata is missing.\");\n  if (comparison === 0) { emit(options, { mode, currentVersion, targetVersion, changed: false }, `paperclipai ${targetVersion} is already active.`); return; }\n  if (comparison < 0 && options.yes !== true) { const confirmed = await (overrides.confirm ?? defaultConfirm)(`Downgrade paperclipai from ${currentVersion} to ${targetVersion}?`); if (!confirmed) throw new Error(\"Downgrade cancelled. Re-run with --yes to confirm explicitly.\"); }\n  if (options.dryRun) { emit(options, { mode, currentVersion, targetVersion, action: comparison < 0 ? \"downgrade\" : \"update\", backup: options.backup !== false, dryRun: true }, `Would ${comparison < 0 ? \"downgrade\" : \"update\"} paperclipai ${currentVersion} → ${targetVersion}${options.backup === false ? \" without a backup\" : \" after a database backup\"}.`); return; }\n  if (options.backup !== false) await runPreUpdateBackup(options, overrides.backup ?? (() => dbBackupCommand({})), overrides.hasInstanceData);\n  const installed = await withInstallStoreLock(async () => {\n    const payload = await installNpmPayload(targetVersion, runCommand, paths);\n    const record: InstallRecord = { source: \"npm\", version: targetVersion, channel: request.channel, 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 {\n    restarted = await (overrides.restartActiveService ?? restartActiveManagedService)(targetVersion);\n  } catch (error) {\n    return rollbackAfterServiceValidationFailure(\n      paths,\n      overrides.restartActiveService ?? restartActiveManagedService,","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/commands/update.ts#L226-L262","documentation":"Thrown by updateCommand on the managed-npm update path when readInstallManifest returned null. By this point the mode is 'managed' (so the executable resolves through the store's current symlink) but no manifest.json exists, meaning the store is in an inconsistent state: a working symlink without the metadata needed to compute the next manifest, channel, or previous[] history.","triggerScenarios":"updateCommand reaches line 244 in managed mode with a null manifest — e.g. the current symlink exists but manifest.json was deleted, or the store was partially reconstructed. Distinct from error 184 which fires inside rollbackManagedInstall; this one fires on the forward-update path.","commonSituations":"Manifest file removed/renamed by accident. Store migrated/copied without manifest.json. A prior failed update that wrote the symlink but not the manifest. Hand-edited store leaving it inconsistent.","solutions":["Reinstall cleanly: remove the broken store directory and run `paperclipai install` to regenerate both the symlink and manifest.","Restore manifest.json from a backup into the managed install store root.","If only the manifest is missing and the payload dir is intact, write a minimal manifest (source, version, channel, payloadPath, previous: []) matching the current symlink target."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nimport { resolveInstallStorePaths, readInstallManifest } from '../install-store.js';\nfunction managedStoreConsistent(): boolean {\n  const paths = resolveInstallStorePaths();\n  return fs.existsSync(paths.cliRoot) && readInstallManifest(paths) !== null;\n}\n// if (!managedStoreConsistent()) { /* reinstall before update */ }","typeGuard":null,"tryCatchPattern":"try {\n  await updateCommand(options);\n} catch (error) {\n  if (error instanceof Error && error.message === 'Managed install metadata is missing.') {\n    // store is inconsistent — reinstall to regenerate manifest, then retry update\n    await installCommand(/* ... */);\n    await updateCommand(options);\n  } else throw error;\n}","preventionTips":["Never partially delete the managed install store; remove it fully or leave it intact.","Back up manifest.json alongside the store.","After copying/migrating a store, verify readInstallManifest returns non-null before updating."],"tags":["managed-install","metadata","update"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}