{"record":{"id":"d5f23f42de817198","repo":"gitbutlerapp/gitbutler","slug":"existing-gitmeta-key-transcript-key-is-not-a-l","errorCode":null,"errorMessage":"existing GitMeta key '{transcript_key}' is not a list","messagePattern":"existing GitMeta key '(.+?)' is not a list","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-agentlog/src/gitmeta/read_support.rs","lineNumber":79,"sourceCode":"        bail!(\"existing GitMeta key '{detail_key}' is not a string\");\n    };\n    serde_json::from_str(&detail)\n        .with_context(|| format!(\"existing GitMeta key '{detail_key}' has invalid JSON\"))\n        .map(Some)\n}\n\npub(super) fn read_transcript_entries(\n    handle: &SessionTargetHandle<'_>,\n    transcript_key: &str,\n) -> Result<Vec<ListEntry>> {\n    let Some(transcript_value) = handle\n        .get_value(transcript_key)\n        .with_context(|| format!(\"failed to read GitMeta key '{transcript_key}'\"))?\n    else {\n        bail!(\"existing GitMeta key '{transcript_key}' is missing\");\n    };\n    let MetaValue::List(transcript_entries) = transcript_value else {\n        bail!(\"existing GitMeta key '{transcript_key}' is not a list\");\n    };\n    Ok(transcript_entries)\n}\n\npub(super) fn transcript_records_by_hash<T>(\n    entries: Vec<ListEntry>,\n    needed_hashes: &std::collections::HashSet<String>,\n    mut parse_record: impl FnMut(&str) -> Option<(String, T)>,\n) -> HashMap<String, T> {\n    let mut records = HashMap::with_capacity(needed_hashes.len());\n    for entry in entries.into_iter().rev() {\n        let Some((record_hash, record)) = parse_record(&entry.value) else {\n            continue;\n        };\n        if !needed_hashes.contains(&record_hash) {\n            continue;\n        }\n        records.insert(record_hash, record);","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-agentlog/src/gitmeta/read_support.rs#L61-L97","documentation":"The session transcript is stored as a `MetaValue::List` of timestamped entries under `<session_prefix>:transcript`. `read_transcript_entries` accepts a missing key as fatal (error 86) and a non-List variant as this error. It means the transcript key exists but holds a String or Set instead of the expected entry list.","triggerScenarios":"`show`/record reassembly on a session whose `:transcript` key was written as a non-List variant — typically version skew between but-agentlog writers or a mangled GitMeta merge.","commonSituations":"Mixed `but` versions capturing the same session; push-conflict resolution reshaping the value; manual key edits; restoring metadata from an incompatible backup.","solutions":["Run `but agentlog sync` and retry","Re-capture and re-publish the session so the transcript key is rewritten as a List","Align `but` versions on all machines sharing the GitMeta remote","Restore the last List-valued version from the GitMeta ref history, or delete the malformed key"],"exampleFix":"// before\nlet entries = read_transcript_entries(&handle, &transcript_key)?;\n\n// after: narrow the variant explicitly\nlet entries = match handle.get_value(&transcript_key)? {\n    Some(MetaValue::List(entries)) => entries,\n    Some(_) => bail!(\"transcript key '{transcript_key}' is not a list; re-sync required\"),\n    None => Vec::new(),\n};","handlingStrategy":"type-guard","validationCode":"// Confirm the transcript key holds a List before reassembly\nif let Some(value) = handle.get_value(&transcript_key)? {\n    if !matches!(value, MetaValue::List(_)) { /* malformed; sync or re-capture */ }\n}","typeGuard":"fn transcript_is_list(v: Option<&MetaValue>) -> bool {\n    matches!(v, Some(MetaValue::List(_)))\n}","tryCatchPattern":"match read_transcript_entries(&handle, &transcript_key) {\n    Ok(e) => e,\n    Err(err) if err.to_string().contains(\"is not a list\") => Vec::new(),\n    Err(err) => return Err(err),\n}","preventionTips":["One but version per metadata remote","Re-capture to rewrite transcripts after format changes","Never reshape GitMeta values with external tooling"],"tags":["gitmeta","type-mismatch","transcript","rust"],"backgroundTag":"stored-data-type-mismatch","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}