gitbutlerapp/gitbutler · error

existing GitMeta key '{transcript_key}' is missing

Error message

existing GitMeta key '{transcript_key}' is missing

What it means

`read_transcript_entries` reads the session's raw transcript under `<session_prefix>:transcript` and requires it to exist as a `MetaValue::List` — unlike the preview path in `session_outline.rs`, absence is fatal here. The bail fires when a session is expected to have a transcript but the key is missing entirely. That points to an incomplete publish, an older data format, or a partially synced state.

Source

Thrown at crates/but-agentlog/src/gitmeta/read_support.rs:76

        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");
    };
    Ok(transcript_entries)
}

pub(super) fn transcript_records_by_hash<T>(
    entries: Vec<ListEntry>,
    needed_hashes: &std::collections::HashSet<String>,
    mut parse_record: impl FnMut(&str) -> Option<(String, T)>,
) -> HashMap<String, T> {
    let mut records = HashMap::with_capacity(needed_hashes.len());
    for entry in entries.into_iter().rev() {
        let Some((record_hash, record)) = parse_record(&entry.value) else {
            continue;
        };
        if !needed_hashes.contains(&record_hash) {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Run `but agentlog sync` to complete the state and retry
  2. Re-capture the session via the hook (new agent activity re-stores `:transcript`) and re-publish
  3. If the original transcript file still exists locally (e.g. ~/.claude or ~/.codex session log), re-run capture from it
  4. If the transcript is gone for good, treat the session as summary-only: delete or skip its transcript-dependent records

Example fix

// before
let entries = read_transcript_entries(&handle, &transcript_key)?;

// after: check existence first and degrade gracefully
let entries = match handle.get_value(&transcript_key)? {
    Some(MetaValue::List(entries)) => entries,
    Some(_) => bail!("transcript key '{transcript_key}' has the wrong type"),
    None => { eprintln!("session has no stored transcript; skipping"); Vec::new() }
};
Defensive patterns

Strategy: validation

Validate before calling

// Check transcript presence before record reassembly
let transcript_key = format!("{session_prefix}:transcript");
if handle.get_value(&transcript_key)?.is_none() {
    eprintln!("no stored transcript for session; run sync or re-capture");
}

Type guard

fn transcript_present(handle: &SessionTargetHandle<'_>, key: &str) -> Result<bool> {
    Ok(handle.get_value(key)?.is_some())
}

Try / catch

match read_transcript_entries(&handle, &transcript_key) {
    Ok(entries) => entries,
    Err(err) if err.to_string().contains("is missing") => Vec::new(),
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Record-reassembly paths (`show`, record-hash lookups) on a session that has turns/summaries but no `:transcript` key — e.g. the session was published by a writer that did not store transcripts, or a pull/push cycle dropped it.

Common situations: Sessions captured before the transcript-storage feature; interrupted or conflicted GitMeta sync; a turn list rewritten without the matching transcript; deleting keys manually while debugging.

Related errors


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