gitbutlerapp/gitbutler · error

existing GitMeta key

Error message

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

What it means

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.

Solutions

  1. 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).
  2. Verify the writer version: re-record the session transcript with the current but-agentlog so the key is written as a List.
  3. If hit during preview only, regenerate session outline data by re-running the recording/ingest for that session.

Example fix

// before
let MetaValue::List(entries) = value else {
    bail!("existing GitMeta key '{transcript_key}' is not a list");
};
// after (tolerate a JSON-encoded string)
let entries = match value {
    MetaValue::List(entries) => entries,
    MetaValue::String(s) => serde_json::from_str(&s)?,
    other => bail!("existing GitMeta key '{transcript_key}' has unexpected type: {other:?}"),
};
Defensive patterns

Strategy: validation

Validate before calling

// Rust: before relying on previews, check the stored shape
let value = meta.get_value(&transcript_key)?;
if let Some(v) = &value {
    if !matches!(v, MetaValue::List(_)) {
        // treat as stale: delete or migrate the key before reading
        meta.delete_value(&transcript_key)?;
    }
}

Type guard

fn is_list(v: &MetaValue) -> bool { matches!(v, MetaValue::List(_)) }

Try / catch

match session_outline(...).context("reading session previews") {
    Ok(p) => p,
    Err(e) if e.to_string().contains("is not a list") => SessionPreviews::default(),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling related_session_outline -> session_previews when the GitMeta key '{transcript_key}' exists via get_value but its MetaValue variant is not MetaValue::List.

Common situations: 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.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/f5032bb9b5ff81d7. Report an issue: GitHub.

Appendix: 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 58e5313667)