{"record":{"id":"6a61f80077fea12e","repo":"sinelaw/fresh","slug":"unarchive-failed","errorCode":null,"errorMessage":"unarchive failed","messagePattern":"unarchive failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fresh-editor/plugins/orchestrator.ts","lineNumber":10751,"sourceCode":"  // Match on the archived path first (unique), then the display name, which\n  // is what a human reads off the listing. A name shared by two archived\n  // workspaces resolves to the newest, matching the listing's own order.\n  let match: ArchivedSession | null = null;\n  for (const { manifest } of scanArchiveManifests()) {\n    for (const e of manifest.sessions) {\n      if (e.root === want) {\n        match = e;\n        break;\n      }\n      if (e.label === want && (!match || e.archived_at > match.archived_at)) {\n        match = e;\n      }\n    }\n    if (match && match.root === want) break;\n  }\n  if (!match) return false;\n  const res = await unarchiveOne(match);\n  if (!res.ok) throw new Error(res.err || \"unarchive failed\");\n  // The manifest changed, so push it the same way the archive path does.\n  if (res.repoRoot) triggerSyncAsync(res.repoRoot);\n  refreshOpenDialog();\n  return true;\n}\n\nfunction apiSetDockView(view: \"card\" | \"compact\"): void {\n  if (view !== \"card\" && view !== \"compact\") {\n    throw new Error(`unknown dock view: ${view}`);\n  }\n  dockView = view;\n  // Pin it for the rest of the session, exactly as the toolbar's \"view\"\n  // button does — the `defaultView` setting only decides where the dock\n  // *starts*, so without the override a later re-open would undo this.\n  dockViewOverride = view;\n  refreshOpenDialog();\n}\n","sourceCodeStart":10733,"sourceCodeEnd":10769,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-editor/plugins/orchestrator.ts#L10733-L10769","documentation":"The unarchive-by-root API locates the archived workspace matching a wanted repo root and calls unarchiveOne. If the unarchive operation reports failure (res.ok falsy), it throws res.err when a reason is provided, otherwise the generic 'unarchive failed'. This means the workspace was found but the unarchive itself did not complete.","triggerScenarios":"Calling the unarchive API for a repo root that resolves to an archived workspace whose unarchive fails — target path conflicts (a directory already exists at the restore location), manifest write failures, or cross-machine sync problems.","commonSituations":"Restoring over an existing checkout of the same repo; disk/permission problems at the restore target; concurrent modification of the archive manifest from another machine.","solutions":["Read res.err / the thrown message for the specific cause and fix it (e.g. move the existing directory at the restore path out of the way)","Ensure no directory currently occupies the workspace's restore location before unarchiving","Retry after resolving manifest conflicts, letting triggerSyncAsync push the corrected manifest"],"exampleFix":"// before\nawait api.unarchiveByRoot(repoRoot);\n// after\ntry {\n  await api.unarchiveByRoot(repoRoot);\n} catch (e) {\n  if (fs.existsSync(expectedPath)) {\n    fs.renameSync(expectedPath, expectedPath + \".bak\");\n    await api.unarchiveByRoot(repoRoot);\n  } else { throw e; }\n}","handlingStrategy":"try-catch","validationCode":"const targetPath = expectedRestorePath(repoRoot);\nif (fs.existsSync(targetPath)) {\n  throw new Error(`restore path already occupied: ${targetPath}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.unarchiveByRoot(repoRoot);\n} catch (e) {\n  const msg = String(e);\n  if (msg.includes(\"unarchive failed\") && !msg.includes(\"Error: unarchive failed: \")) {\n    // no detail: check restore path and manifest, then retry\n    clearRestorePath(repoRoot);\n    await api.unarchiveByRoot(repoRoot);\n  } else throw e;\n}","preventionTips":["Ensure the restore location is free before unarchiving","Keep the archive manifest in sync across machines to avoid stale entries","Surface res.err by catching early rather than letting it collapse to the generic message","Verify the workspace was actually found (the API returns false when not) before interpreting the throw as an unarchive failure"],"tags":["typescript","unarchive","workspace-restore"],"backgroundTag":"operation-failed","analyzedSha":"67894ca5463dbd7a89bb31add4627c27d6b79d83","analyzedAt":"2026-09-13T15:04:03.701Z","contentChangedAt":"2026-09-13T15:04:03.701Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}