{"record":{"id":"1663bc71d1938170","repo":"gitbutlerapp/gitbutler","slug":"worktree-name-is-archived","errorCode":null,"errorMessage":"Worktree {name} is archived","messagePattern":"Worktree (.+?) is archived","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-api/src/worktrees.rs","lineNumber":68,"sourceCode":"    }\n    Ok(())\n}\n\n/// Look up the *active* linked worktree named `name`.\n///\n/// Every command here operates on active worktrees only - an archived one is\n/// hidden from the graph, so operations against it could not be materialized.\n///\n/// Must not be called while a database handle is borrowed, see\n/// [`but_ctx::Context::worktrees_with_state()`].\nfn active_worktree(ctx: &but_ctx::Context, name: &str) -> Result<WorktreeEntry> {\n    let worktree = ctx\n        .worktrees_with_state()?\n        .into_iter()\n        .find(|worktree| worktree.name == name.as_bytes())\n        .with_context(|| format!(\"Worktree {name} does not exist\"))?;\n    if worktree.archived {\n        bail!(\"Worktree {name} is archived\");\n    }\n    if ctx.worktree_head(worktree.name.as_bstr())?.is_none() {\n        // Unborn, workspace-ref checkout, or broken - nothing to operate on.\n        bail!(\"Worktree {name} has no usable HEAD\");\n    }\n    Ok(worktree)\n}\n\n/// Open the checkout that `source` reads its changes from, returning its stable\n/// name along with a plain from-disk open of it, or `None` for the main worktree.\n///\n/// Callers turn this into a [`ChangeSource`](but_workspace::commit::ChangeSource)\n/// for the duration of an editor-backed operation.\n///\n/// Must not be called while a database handle is borrowed, see\n/// [`but_ctx::Context::worktrees_with_state()`].\npub(crate) fn open_changes_source(\n    ctx: &but_ctx::Context,","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-api/src/worktrees.rs#L50-L86","documentation":"active_worktree() resolves a linked worktree by its stable name (directory under $GIT_COMMON_DIR/worktrees/) and rejects archived ones: archived worktrees are hidden from the workspace graph, so operations against them could not be materialized. The lookup first fails with 'Worktree {name} does not exist' for unknown names; an existing-but-archived worktree fails here, before the follow-up 'no usable HEAD' check.","triggerScenarios":"Passing the name of a currently archived worktree to any worktree command (set head, commit from ChangeSource::Worktree('name'), etc.), or a race where the worktree was archived between your worktrees_list() call and the operation.","commonSituations":"UI holding a stale listing after the user archived worktrees elsewhere (projects predating GitButler worktree support often archive en masse); scripts iterating an old listing; forgetting that archived entries live in listing.archived, not listing.active.","solutions":["Unarchive it first: worktree_set_archived(name, false), then retry the operation","Refresh with worktrees_list() and only operate on names from listing.active","Drop the stale entry from your UI/state when this fires - the worktree is intentionally hidden"],"exampleFix":"// before\nawait someWorktreeOperation(name); // name was archived since listing\n\n// after\nconst listing = await api.worktreesList();\nif (listing.active.some(w => w.name === name)) {\n  await someWorktreeOperation(name);\n} else if (listing.archived.some(w => w.name === name)) {\n  await api.worktreeSetArchived(name, false); // opt back in, then operate\n}","handlingStrategy":"validation","validationCode":"// Only operate on names from the active section of a fresh listing\nconst listing = await api.worktreesList();\nconst isActive = (name: string) => listing.active.some(w => w.name === name);\nif (!isActive(name)) throw new Error(`worktree ${name} is archived or gone`);","typeGuard":"interface WorktreeRef { name: string; archived: boolean }\nfunction isOperableWorktree(w: WorktreeRef, name: string): boolean {\n  return w.name === name && !w.archived;\n}","tryCatchPattern":"try {\n  await someWorktreeOperation(name);\n} catch (err) {\n  if (String(err).includes('is archived')) {\n    await api.worktreeSetArchived(name, false); // opt back in if intended\n    await someWorktreeOperation(name);\n  } else throw err;\n}","preventionTips":["Drive worktree operations from listing.active (fresh worktrees_list), never from a cached list","Watch for sibling errors: 'does not exist' (unknown name) and 'has no usable HEAD' (unborn/broken) come from the same lookup","When the user archives a worktree, immediately drop it from any actionable UI state"],"tags":["worktrees","archived-state","stale-state","experimental"],"backgroundTag":"archived-worktree-access","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}