{"record":{"id":"e0bae2a0976c001a","repo":"gitbutlerapp/gitbutler","slug":"starting-index-state-can-only-be-fetched-while-in","errorCode":null,"errorMessage":"Starting index state can only be fetched while in edit mode","messagePattern":"Starting index state can only be fetched while in edit mode","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/gitbutler-edit-mode/src/lib.rs","lineNumber":438,"sourceCode":"}\n\n#[derive(Serialize, Debug, Clone)]\n#[cfg_attr(feature = \"export-schema\", derive(schemars::JsonSchema))]\n#[serde(rename_all = \"camelCase\")]\npub struct ConflictEntryPresence {\n    pub ours: bool,\n    pub theirs: bool,\n    pub ancestor: bool,\n}\n#[cfg(feature = \"export-schema\")]\nbut_schemars::register_sdk_type!(ConflictEntryPresence);\n\npub(crate) fn starting_index_state(\n    ctx: &Context,\n    perm: &RepoShared,\n) -> Result<Vec<(TreeChange, Option<ConflictEntryPresence>)>> {\n    let OperatingMode::Edit(metadata) = operating_mode(ctx, perm)? else {\n        bail!(\"Starting index state can only be fetched while in edit mode\")\n    };\n\n    let repo = &*ctx.repo.get()?;\n    let commit = repo.find_commit(metadata.commit_oid)?;\n    let commit_parent_tree = if commit.is_conflicted() {\n        but_core::Commit::try_from(commit.clone())?\n            .tree_id_or_kind(but_core::commit::TreeKind::Base)?\n            .detach()\n    } else {\n        commit\n            .parent_ids()\n            .next()\n            .context(\"edited commit had no parent\")?\n            .object()?\n            .try_into_commit()?\n            .tree_id()?\n            .detach()\n    };","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/gitbutler-edit-mode/src/lib.rs#L420-L456","documentation":"`starting_index_state` reconstructs the index state recorded when edit mode was entered, which only exists while the project operates in `OperatingMode::Edit`. It bails immediately in open-workspace mode (or any other mode). It is part of the edit-mode read APIs used to list conflicts and index entries.","triggerScenarios":"Calling the starting-index/conflict listing path when the project is in open workspace mode; or when an operating-mode switch happened between the caller's own check and this call.","commonSituations":"App code listing conflicts without first entering edit mode; races between mode switching and background refreshes; stale mode state after a failed edit-mode session.","solutions":["Enter edit mode first, then query the starting index state","Guard the call with `in_edit_mode(ctx, perm)?` or call `ensure_edit_mode(ctx, perm)` which fails with a clear mode error","Re-read the operating mode if a concurrent mode switch is possible"],"exampleFix":"// before\nlet state = starting_index_state(&ctx, &perm)?;\n\n// after\nensure_edit_mode(&ctx, &perm)?; // Err(\"Expected to be in edit mode\") when wrong\nlet state = starting_index_state(&ctx, &perm)?;","handlingStrategy":"validation","validationCode":"if !in_edit_mode(&ctx, &perm)? {\n    return Err(anyhow::anyhow!(\"starting index state requires edit mode\"));\n}","typeGuard":"fn can_query_edit_mode_state(mode: &OperatingMode) -> bool {\n    matches!(mode, OperatingMode::Edit(_))\n}","tryCatchPattern":"match starting_index_state(&ctx, &perm) {\n    Err(err) if err.to_string().contains(\"while in edit mode\") => { /* enter edit mode or skip */ }\n    other => other,\n}","preventionTips":["Gate every edit-mode read behind in_edit_mode/ensure_edit_mode","Reload the operating mode after any mode-switching operation"],"tags":["rust","gitbutler","edit-mode","operating-mode","state-guard"],"backgroundTag":"wrong-operating-mode","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}