{"record":{"id":"3d1bd1bcb317382f","repo":"linshenkx/prompt-optimizer","slug":"unable-to-safely-analyze-remote-snapshot-assets-be","errorCode":null,"errorMessage":"Unable to safely analyze remote snapshot assets because ${failures.length} snapshot manifest(s) could not be read. First failure: ${firstFailure.path}: ${firstFailure.message}","messagePattern":"Unable to safely analyze remote snapshot assets because (.+?) snapshot manifest\\(s\\) could not be read\\. First failure: (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/src/utils/remote-snapshot-backup.ts","lineNumber":655,"sourceCode":"  const entries = await objectStore.list(joinRemotePath(REMOTE_SNAPSHOT_ROOT, 'snapshots'))\n  const manifestEntries = entries.filter((entry) => entry.path.endsWith(`/${MANIFEST_FILE_NAME}`))\n  const manifests: RemoteSnapshotManifest[] = []\n  const failures: Array<{ path: string; message: string }> = []\n\n  for (const entry of manifestEntries) {\n    try {\n      manifests.push(parseManifest(await objectStore.getText(entry.path)))\n    } catch (error) {\n      failures.push({\n        path: entry.path,\n        message: (error as Error).message || String(error),\n      })\n    }\n  }\n\n  if (failures.length > 0) {\n    const firstFailure = failures[0]\n    throw new Error(\n      `Unable to safely analyze remote snapshot assets because ${failures.length} snapshot manifest(s) could not be read. First failure: ${firstFailure.path}: ${firstFailure.message}`,\n    )\n  }\n\n  return manifests\n}\n\nconst isCleanupCandidateOldEnough = (\n  entry: Pick<RemoteObjectEntry, 'updatedAt'>,\n  nowMs: number,\n  minimumAgeMs: number,\n): boolean => {\n  if (minimumAgeMs <= 0) return true\n  if (!entry.updatedAt) return false\n  const updatedAtMs = Date.parse(entry.updatedAt)\n  return Number.isFinite(updatedAtMs) && nowMs - updatedAtMs >= minimumAgeMs\n}\n","sourceCodeStart":637,"sourceCodeEnd":673,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/ui/src/utils/remote-snapshot-backup.ts#L637-L673","documentation":"Thrown by analyzeRemoteSnapshotAssetCleanup in remote-snapshot-backup.ts after it attempts to read every remote snapshot manifest and collects failures. Because cleanup analysis must classify each snapshot's assets to decide what is safe to delete, an unreadable manifest makes the whole analysis unsafe, so the function aborts with the count and the first failing path/message.","triggerScenarios":"Calling analyzeRemoteSnapshotAssetCleanup (or cleanupRemoteSnapshotAssets, which calls it first) when one or more snapshot manifest JSON objects on the remote object store are missing, corrupted, truncated, or return access/network errors during listing+get.","commonSituations":"A previous backup upload was interrupted leaving a partial manifest; the remote bucket was edited/deleted manually; credentials lost read permission on some keys; transient network errors or bucket rate limits during manifest reads.","solutions":["Check the reported first failure path and message to see whether it is a network/permission problem or corrupted JSON","Retry the operation if the message indicates a transient network failure","Repair or remove the broken snapshot entry (delete the orphaned manifest/asset keys for that snapshot) and re-run the analysis","Verify object-store credentials and bucket policy grant read access to all snapshot prefixes"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-validate manifest readability before analysis\nconst manifests = await Promise.all(snapshotIds.map(async (id) => {\n  try { return await store.get(`snapshots/${id}/manifest.json`) } catch { return null }\n}))\nif (manifests.some((m) => m === null)) skipCleanup()","typeGuard":"const isReadableManifest = (value: unknown): value is SnapshotManifest =>\n  typeof value === 'object' && value !== null && 'id' in value && 'assets' in value","tryCatchPattern":"try {\n  await cleanupRemoteSnapshotAssets(store, onProgress)\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Unable to safely analyze')) {\n    // log and skip cleanup; do not delete anything\n  } else throw error\n}","preventionTips":["Ensure backups fully complete so manifests are written atomically","Monitor failed upload jobs and clean up partial snapshots","Keep bucket credentials valid and scoped to the snapshot prefix"],"tags":["remote-storage","snapshot","manifest","cleanup"],"backgroundTag":"corrupted-manifest-file","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}