gitbutlerapp/gitbutler · error
existing GitMeta key '{detail_key}' is not a string
Error message
existing GitMeta key '{detail_key}' is not a string What it means
`enrich_incomplete_turn` scans stored turn details for incoming record hashes using `let Some(MetaValue::String(detail)) = ...`; the else branch fires both when `<session_prefix>:turn:<turn_key>` is missing and when it holds a non-String variant, but the message only says 'is not a string'. So this error actually covers 'detail key absent or wrong shape' during the write-path turn-enrichment pass.
Source
Thrown at crates/but-agentlog/src/gitmeta/write.rs:705
format!("existing GitMeta key '{turns_key}' has invalid turn summary")
})?;
if !matches!(
summary["environment_snapshot_status"].as_str(),
Some("failed" | "partial")
) || summary["source_key"].as_str() != Some(source_key)
{
continue;
}
let turn_key = summary["turn_key"]
.as_str()
.context("existing turn summary is missing turn_key")?
.to_owned();
let detail_key = format!("{session_prefix}:turn:{turn_key}");
let Some(MetaValue::String(detail)) = handle
.get_value(&detail_key)
.with_context(|| format!("failed to read GitMeta key '{detail_key}'"))?
else {
bail!("existing GitMeta key '{detail_key}' is not a string");
};
let detail: Value = serde_json::from_str(&detail)
.with_context(|| format!("existing GitMeta key '{detail_key}' has invalid JSON"))?;
if !detail_records_are_incoming(&detail, &incoming_record_hashes) {
continue;
}
let is_new_best = best
.as_ref()
.is_none_or(|(timestamp, best_index, _, _, _)| {
entry
.timestamp
.cmp(timestamp)
.then_with(|| index.cmp(best_index))
.is_gt()
});
if is_new_best {
best = Some((entry.timestamp, index, summary, turn_key, detail));
}View on GitHub (pinned to caf1f223d3)
Solutions
- Run `but agentlog sync` to complete the partial state, then retry capture/publish
- Produce one more agent turn in that session and let the hook re-capture — enrichment then works from complete data
- Align `but` versions on all writers of that session
- Inspect GitMeta history for `gitbutler:agent-session:*:turn:<turn_key>` and restore or delete the stale entry
Example fix
// before: one arm covers both missing and wrong-type
let Some(MetaValue::String(detail)) = handle.get_value(&detail_key)? else {
bail!("existing GitMeta key '{detail_key}' is not a string");
};
// after: distinguish the cases and skip unenrichable turns
let detail = match handle.get_value(&detail_key)? {
Some(MetaValue::String(detail)) => detail,
Some(_) => { warn!("turn detail '{detail_key}' has wrong type; skipping"); continue }
None => { warn!("turn detail '{detail_key}' missing; skipping"); continue }
}; Defensive patterns
Strategy: validation
Validate before calling
// Before the write path, verify each referenced turn detail key
let detail_key = format!("{session_prefix}:turn:{turn_key}");
match handle.get_value(&detail_key)? {
Some(MetaValue::String(_)) => { /* enrichable */ }
_ => { /* missing or wrong shape; sync or skip enrichment */ }
} Type guard
fn enrichable_detail(v: Option<&MetaValue>) -> bool {
matches!(v, Some(MetaValue::String(_)))
} Try / catch
match enrich_incomplete_turn(&handle, ...) {
Ok(Some(turn)) => turns.push(turn),
Err(err) if err.to_string().contains(":turn:") => {
eprintln!("turn detail unavailable; continuing without enrichment")
}
Err(err) => return Err(err),
} Prevention
- Let publishes complete; interrupted writes are the main source of absent detail keys
- Sync after failed publishes before capturing again
- Align but versions to avoid reshaped detail values
When it happens
Trigger: The capture/publish write path enriching an incomplete turn where the `:turn:<turn_key>` key referenced by the stored turns list is absent (torn earlier write, partial sync) or stored as a non-String variant (version skew).
Common situations: An earlier publish was interrupted between writing the turns list and the turn detail keys; mixed `but` versions wrote the session; a conflicted GitMeta merge dropped or reshaped detail keys.
Related errors
- existing GitMeta key '{detail_key}' is not a string
- existing GitMeta key '{turns_key}' is not a list
- existing GitMeta key '{record_hashes_key}' is not a set
- existing GitMeta key '{associated_targets_key}' is not a str
- existing GitMeta key '{updated_at_key}' is not a string
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/c0e7fb21cd86c653.
Report an issue: GitHub.