gitbutlerapp/gitbutler · error

existing GitMeta key '{transcript_key}' is not a list

Error message

existing GitMeta key '{transcript_key}' is not a list

What it means

When building skim previews, `session_outline` reads the transcript key only if there are hashes to preview; a missing key returns empty previews, but a key that exists and is not a `MetaValue::List` bails. So this error means the `:transcript` value has the wrong shape while the preview path is trying to extract the latest user/assistant messages.

Source

Thrown at crates/but-agentlog/src/gitmeta/session_outline.rs:90

fn session_previews(
    handle: &SessionTargetHandle<'_>,
    session_prefix: &str,
    preview_hashes: &HashSet<String>,
) -> Result<SessionPreviews> {
    if preview_hashes.is_empty() {
        return Ok(SessionPreviews::default());
    }

    let transcript_key = format!("{session_prefix}:transcript");
    let Some(value) = handle
        .get_value(&transcript_key)
        .with_context(|| format!("failed to read GitMeta key '{transcript_key}'"))?
    else {
        return Ok(SessionPreviews::default());
    };
    let MetaValue::List(entries) = value else {
        bail!("existing GitMeta key '{transcript_key}' is not a list");
    };

    let mut previews = SessionPreviews::default();
    let mut entries = entries;
    entries.sort_by_key(|entry| entry.timestamp);
    for entry in entries.into_iter().rev() {
        if previews.latest_user.is_some() && previews.latest_assistant.is_some() {
            break;
        }

        let Ok(record) = serde_json::from_str::<Value>(&entry.value) else {
            continue;
        };
        let Some(record_hash) = record["record_hash"].as_str() else {
            continue;
        };
        if !preview_hashes.contains(record_hash) {
            continue;

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Run `but agentlog sync` and retry skim
  2. Re-capture and re-publish the affected session to rewrite the transcript key
  3. Align `but` versions across machines
  4. Restore the last List-valued version from GitMeta ref history or delete the malformed key (previews degrade to empty)

Example fix

// before
let MetaValue::List(entries) = value else { bail!("...") };

// after: fall back to empty previews when the shape is wrong
let entries = match value {
    MetaValue::List(entries) => entries,
    _ => return Ok(SessionPreviews::default()),
};
Defensive patterns

Strategy: type-guard

Validate before calling

// Before building previews, verify the transcript variant
if let Some(value) = handle.get_value(&transcript_key)? {
    if !matches!(value, MetaValue::List(_)) { /* fall back to empty previews */ }
}

Type guard

fn previewable_transcript(v: Option<&MetaValue>) -> bool {
    matches!(v, Some(MetaValue::List(_)))
}

Try / catch

let entries = match handle.get_value(&transcript_key)? {
    Some(MetaValue::List(entries)) => entries,
    _ => return Ok(SessionPreviews::default()),
};

Prevention

When it happens

Trigger: `but agentlog skim` building session previews for a session whose `gitbutler:agent-session:<status>:<key>:transcript` key holds a String or Set variant instead of the List of timestamped entries.

Common situations: Version-skewed writers on the same GitMeta remote; conflicted merges of the metadata ref; manual edits to GitMeta keys; sessions restored from incompatible backups.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/f5032bb9b5ff81d7. Report an issue: GitHub.