{"record":{"id":"0b8c15b3981e441b","repo":"gitbutlerapp/gitbutler","slug":"existing-gitmeta-key-detail-key-is-missing","errorCode":null,"errorMessage":"existing GitMeta key '{detail_key}' is missing","messagePattern":"existing GitMeta key '(.+?)' is missing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-agentlog/src/gitmeta/read_support.rs","lineNumber":45,"sourceCode":"    };\n    let MetaValue::List(turn_entries) = turns_value else {\n        bail!(\"existing GitMeta key '{turns_key}' is not a list\");\n    };\n\n    let summaries = stored_turn_summary_entries(turn_entries, turns_key)?\n        .into_iter()\n        .map(|entry| entry.summary)\n        .collect::<Vec<_>>();\n\n    Ok(summaries)\n}\n\npub(super) fn read_turn_detail(\n    handle: &SessionTargetHandle<'_>,\n    detail_key: &str,\n) -> Result<StoredTurnDetail> {\n    let Some(detail) = read_optional_turn_detail(handle, detail_key)? else {\n        bail!(\"existing GitMeta key '{detail_key}' is missing\");\n    };\n    Ok(detail)\n}\n\npub(super) fn read_optional_turn_detail(\n    handle: &SessionTargetHandle<'_>,\n    detail_key: &str,\n) -> Result<Option<StoredTurnDetail>> {\n    let Some(value) = handle\n        .get_value(detail_key)\n        .with_context(|| format!(\"failed to read GitMeta key '{detail_key}'\"))?\n    else {\n        return Ok(None);\n    };\n    let MetaValue::String(detail) = value else {\n        bail!(\"existing GitMeta key '{detail_key}' is not a string\");\n    };\n    serde_json::from_str(&detail)","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-agentlog/src/gitmeta/read_support.rs#L27-L63","documentation":"`read_turn_detail` requires the per-turn detail key `<session_prefix>:turn:<turn_key>` to exist; unlike its `read_optional_turn_detail` sibling it does not tolerate absence. The bail fires when the turns list or index references a turn whose detail record is not present in GitMeta. That indicates a torn or partially-synced state between the turn list and the turn detail records.","triggerScenarios":"`but agentlog show <session> --turn <turn_key>` (and record-reassembly paths) where `:turns` lists the turn but `:turn:<turn_key>` is absent — e.g. a publish that wrote the turns list but not every detail key, or a partial pull/sync.","commonSituations":"Interrupted publish or sync (network dropped mid-push); GitMeta push-conflict resolution that dropped detail keys; reading a session captured by an older writer that did not persist turn details; concurrent capture and publish racing on the same session.","solutions":["Run `but agentlog sync` to complete the partial state and retry the exact turn key","Re-run capture for the session (let the agent produce one more turn, then hook + publish) so details are rewritten","Verify the turn key from `show <session>` output and retry with the exact `--turn` value — stale keys from an older turns list are a common cause","If the detail is unrecoverable, delete the stale entry from the `:turns` list or the whole session and re-capture"],"exampleFix":"// before\nlet detail = read_turn_detail(&handle, &detail_key)?;\n\n// after: prefer the optional reader and handle absence explicitly\nlet detail = match read_optional_turn_detail(&handle, &detail_key)? {\n    Some(detail) => detail,\n    None => bail!(\"turn '{detail_key}' has no stored detail; re-run `but agentlog sync`\")\n};","handlingStrategy":"validation","validationCode":"// Verify the turn detail key exists before requesting it\nlet detail_key = format!(\"{session_prefix}:turn:{turn_key}\");\nif handle.get_value(&detail_key)?.is_none() {\n    eprintln!(\"turn {turn_key} detail not synced yet; run `but agentlog sync`\");\n}","typeGuard":"fn turn_detail_exists(handle: &SessionTargetHandle<'_>, key: &str) -> Result<bool> {\n    Ok(handle.get_value(key)?.is_some())\n}","tryCatchPattern":"match read_optional_turn_detail(&handle, &detail_key)? {\n    Some(detail) => detail,\n    None => { /* sync and retry, or surface 'turn detail unavailable' */ }\n}","preventionTips":["Do not interrupt publish mid-run; a torn turns/detail state is the main cause","Run sync after any failed publish before reading turns","Read turn keys from a fresh `show` output, not from stale UI state"],"tags":["gitmeta","missing-key","turn-detail","partial-write","rust"],"backgroundTag":"missing-required-key","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}