{"record":{"id":"f5032bb9b5ff81d7","repo":"gitbutlerapp/gitbutler","slug":"existing-gitmeta-key-transcript-key-is-not-a-l-f5032b","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/session_outline.rs","lineNumber":90,"sourceCode":"\nfn session_previews(\n    handle: &SessionTargetHandle<'_>,\n    session_prefix: &str,\n    preview_hashes: &HashSet<String>,\n) -> Result<SessionPreviews> {\n    if preview_hashes.is_empty() {\n        return Ok(SessionPreviews::default());\n    }\n\n    let transcript_key = format!(\"{session_prefix}:transcript\");\n    let Some(value) = handle\n        .get_value(&transcript_key)\n        .with_context(|| format!(\"failed to read GitMeta key '{transcript_key}'\"))?\n    else {\n        return Ok(SessionPreviews::default());\n    };\n    let MetaValue::List(entries) = value else {\n        bail!(\"existing GitMeta key '{transcript_key}' is not a list\");\n    };\n\n    let mut previews = SessionPreviews::default();\n    let mut entries = entries;\n    entries.sort_by_key(|entry| entry.timestamp);\n    for entry in entries.into_iter().rev() {\n        if previews.latest_user.is_some() && previews.latest_assistant.is_some() {\n            break;\n        }\n\n        let Ok(record) = serde_json::from_str::<Value>(&entry.value) else {\n            continue;\n        };\n        let Some(record_hash) = record[\"record_hash\"].as_str() else {\n            continue;\n        };\n        if !preview_hashes.contains(record_hash) {\n            continue;","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-agentlog/src/gitmeta/session_outline.rs#L72-L108","documentation":"session_previews in crates/but-agentlog/src/gitmeta/session_outline.rs reads a transcript list from GitMeta and expects the stored MetaValue to be a List of turn entries. When the key exists but holds a different value type (String, Set, etc.), the code bails with this message because it cannot sort/iterate the entries. This indicates the GitMeta key was written by a different code path or an older/incompatible schema version.","triggerScenarios":"Calling related_session_outline -> session_previews when the GitMeta key '{transcript_key}' exists via get_value but its MetaValue variant is not MetaValue::List.","commonSituations":"A hand-edited or externally written GitMeta entry; an older schema that stored transcripts as a JSON string; a key collision where another tool wrote a Set or plain value under the same transcript key.","solutions":["Inspect the GitMeta key value and delete/rewrite it so it is a List of transcript entries (e.g. via but meta tooling or git meta branch checkout).","Verify the writer version: re-record the session transcript with the current but-agentlog so the key is written as a List.","If hit during preview only, regenerate session outline data by re-running the recording/ingest for that session."],"exampleFix":"// before\nlet MetaValue::List(entries) = value else {\n    bail!(\"existing GitMeta key '{transcript_key}' is not a list\");\n};\n// after (tolerate a JSON-encoded string)\nlet entries = match value {\n    MetaValue::List(entries) => entries,\n    MetaValue::String(s) => serde_json::from_str(&s)?,\n    other => bail!(\"existing GitMeta key '{transcript_key}' has unexpected type: {other:?}\"),\n};","handlingStrategy":"validation","validationCode":"// Rust: before relying on previews, check the stored shape\nlet value = meta.get_value(&transcript_key)?;\nif let Some(v) = &value {\n    if !matches!(v, MetaValue::List(_)) {\n        // treat as stale: delete or migrate the key before reading\n        meta.delete_value(&transcript_key)?;\n    }\n}","typeGuard":"fn is_list(v: &MetaValue) -> bool { matches!(v, MetaValue::List(_)) }","tryCatchPattern":"match session_outline(...).context(\"reading session previews\") {\n    Ok(p) => p,\n    Err(e) if e.to_string().contains(\"is not a list\") => SessionPreviews::default(),\n    Err(e) => return Err(e),\n}","preventionTips":["Never write GitMeta transcript keys by hand; always use the agentlog writer APIs.","Keep all machines writing to the repo on the same agentlog version.","Treat GitMeta key shapes as schema: document and validate expected MetaValue variants per key prefix."],"tags":["gitmeta","type-mismatch","session-previews"],"backgroundTag":"type-mismatch","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}