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
Each turn's full detail is stored as JSON in a `MetaValue::String` under `<session_prefix>:turn:<turn_key>`. `read_optional_turn_detail` tolerates a missing key but bails when the key exists and is not a String. So this error means the detail record was stored with the wrong MetaValue variant, not that it is absent.
Source
Thrown at crates/but-agentlog/src/gitmeta/read_support.rs:61
) -> Result<StoredTurnDetail> {
let Some(detail) = read_optional_turn_detail(handle, detail_key)? else {
bail!("existing GitMeta key '{detail_key}' is missing");
};
Ok(detail)
}
pub(super) fn read_optional_turn_detail(
handle: &SessionTargetHandle<'_>,
detail_key: &str,
) -> Result<Option<StoredTurnDetail>> {
let Some(value) = handle
.get_value(detail_key)
.with_context(|| format!("failed to read GitMeta key '{detail_key}'"))?
else {
return Ok(None);
};
let MetaValue::String(detail) = value else {
bail!("existing GitMeta key '{detail_key}' is not a string");
};
serde_json::from_str(&detail)
.with_context(|| format!("existing GitMeta key '{detail_key}' has invalid JSON"))
.map(Some)
}
pub(super) fn read_transcript_entries(
handle: &SessionTargetHandle<'_>,
transcript_key: &str,
) -> Result<Vec<ListEntry>> {
let Some(transcript_value) = handle
.get_value(transcript_key)
.with_context(|| format!("failed to read GitMeta key '{transcript_key}'"))?
else {
bail!("existing GitMeta key '{transcript_key}' is missing");
};
let MetaValue::List(transcript_entries) = transcript_value else {
bail!("existing GitMeta key '{transcript_key}' is not a list");View on GitHub (pinned to caf1f223d3)
Solutions
- Run `but agentlog sync` and retry the show command
- Re-capture and re-publish the session to rewrite all `:turn:*` detail keys in the current format
- Align `but` versions across machines sharing the GitMeta remote
- Inspect the GitMeta ref history for the exact `:turn:<turn_key>` key and restore the last String-valued version, or delete it
Example fix
// before
let detail = read_optional_turn_detail(&handle, &detail_key)?;
// after: validate the variant before deserializing
let detail = match handle.get_value(detail_key)? {
None => None,
Some(MetaValue::String(s)) => Some(serde_json::from_str(&s)?),
Some(_) => bail!("turn detail key '{detail_key}' has an unexpected type; re-sync"),
}; Defensive patterns
Strategy: type-guard
Validate before calling
// Narrow the variant before parsing turn detail
match handle.get_value(detail_key)? {
Some(MetaValue::String(raw)) => { let _detail: StoredTurnDetail = serde_json::from_str(&raw)?; }
Some(_) => { /* wrong shape: skip or repair */ }
None => { /* absent */ }
} Type guard
fn turn_detail_is_string(v: Option<&MetaValue>) -> bool {
matches!(v, Some(MetaValue::String(_)))
} Try / catch
match read_optional_turn_detail(&handle, &detail_key) {
Ok(Some(d)) => d,
Ok(None) => Default::default(),
Err(err) if err.to_string().contains("is not a string") => {
eprintln!("skipping malformed turn detail {detail_key}")
}
Err(err) => return Err(err),
} Prevention
- Re-publish sessions after but-agentlog upgrades to rewrite detail keys
- Avoid manual GitMeta edits
- Verify with sync before reading turn details from another machine
When it happens
Trigger: `but agentlog show <session> --turn <turn_key>` hitting a detail key stored as a Set or List — typically after version-skewed writers, corrupted GitMeta state, or a bad merge of the metadata ref.
Common situations: Mixed `but` versions writing the same session; push-conflict resolutions that reshaped values; manual GitMeta edits; sessions restored from a backup made by an incompatible build.
Related errors
- existing GitMeta key '{associated_targets_key}' is not a str
- existing GitMeta key '{detail_key}' is not a string
- existing GitMeta key '{updated_at_key}' is not a string
- existing GitMeta key '{index_key}' is not a set
- existing GitMeta key '{turns_key}' is not a list
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/7a9fd73c71b77518.
Report an issue: GitHub.