{"record":{"id":"10fecf8effeca92d","repo":"gitbutlerapp/gitbutler","slug":"worktree-manipulation-is-not-enabled-featureflags","errorCode":null,"errorMessage":"worktree manipulation is not enabled (featureFlags.worktreeManipulation)","messagePattern":"worktree manipulation is not enabled \\(featureFlags\\.worktreeManipulation\\)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-api/src/worktrees.rs","lineNumber":49,"sourceCode":"    /// The branch the worktree has checked out, or `None` for a detached `HEAD`.\n    #[serde(with = \"but_serde::fullname_lossy_opt\")]\n    pub ref_name: Option<gix::refs::FullName>,\n}\n\n/// All listable linked worktrees, separated by archived state.\n#[derive(Debug, Clone, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct WorktreeListing {\n    /// Non-archived worktrees.\n    pub active: Vec<Worktree>,\n    /// Archived worktrees, hidden from the workspace but still on disk.\n    pub archived: Vec<Worktree>,\n}\n\n/// Fail unless the user opted into worktree manipulation.\nfn ensure_worktree_manipulation_enabled(ctx: &but_ctx::Context) -> Result<()> {\n    if !ctx.settings.feature_flags.worktree_manipulation {\n        bail!(\"worktree manipulation is not enabled (featureFlags.worktreeManipulation)\");\n    }\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 {","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-api/src/worktrees.rs#L31-L67","documentation":"Worktree manipulation in but-api is experimental and opt-in: every command in crates/but-api/src/worktrees.rs (worktrees_list, worktree_set_archived, and the ChangeSource::Worktree commit paths via open_changes_source) calls ensure_worktree_manipulation_enabled() first, which fails unless the user setting featureFlags.worktreeManipulation is enabled. Nothing about the repository is wrong - the feature is simply switched off.","triggerScenarios":"Invoking worktrees_list(), worktree_set_archived(), or any commit-from-worktree flow through Tauri, the SDK, or CLI while settings.feature_flags.worktree_manipulation is false (the default). The check runs before any worktree access, so it fails fast and identically everywhere.","commonSituations":"Fresh install / default settings; SDK or CLI integrations probing worktree APIs without checking the opt-in; users who never enabled the experimental worktree support in GitButler settings.","solutions":["Enable the worktreeManipulation feature flag in GitButler settings, then retry","If writing an integration, detect this error and prompt the user to opt in instead of retrying","Otherwise avoid the worktree APIs - they are experimental and gated for a reason"],"exampleFix":"// before\nconst listing = await api.worktreesList(); // throws when flag off\n\n// after: check the flag first (settings API) and guide the user\nconst flags = await api.getFeatureFlags?.() ?? {};\nif (!flags.worktreeManipulation) {\n  promptEnableExperimental('worktreeManipulation');\n} else {\n  const listing = await api.worktreesList();\n}","handlingStrategy":"validation","validationCode":"// Check the experimental flag before touching worktree APIs\nconst settings = await api.getSettings(); // or your settings transport\nif (!settings.featureFlags?.worktreeManipulation) {\n  promptEnableWorktreeManipulation();\n} else {\n  const listing = await api.worktreesList();\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.worktreesList();\n} catch (err) {\n  if (String(err).includes('featureFlags.worktreeManipulation')) {\n    showOptInNotice('Enable the experimental worktreeManipulation flag to use worktrees');\n  } else throw err;\n}","preventionTips":["Treat worktree APIs as opt-in: surface an 'enable experimental feature' flow, not an error dialog","Do not retry on this error - it only clears when the user changes settings","Gate worktree UI entry points behind the flag so the calls are never attempted"],"tags":["feature-flag","worktrees","settings","experimental"],"backgroundTag":"feature-flag-disabled","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}