gitbutlerapp/gitbutler · error

existing GitMeta key '{updated_at_key}' is not a string

Error message

existing GitMeta key '{updated_at_key}' is not a string

What it means

but-agentlog persists every agent session in GitMeta (a git-backed, remotely-synced key-value store) under keys like `gitbutler:agent-session:<status>:<key>:updated-at`. When building the session list, `session_list_entry` requires that key to be a `MetaValue::String` holding an RFC 3339 timestamp. If the key exists but holds a different MetaValue variant (Set or List), this bail fires and aborts the whole listing.

Source

Thrown at crates/but-agentlog/src/gitmeta/read.rs:63

            | RelatedTarget::Change(key) => key,
        }
    }
}

fn session_list_entry(
    handle: &SessionTargetHandle<'_>,
    status: PublicationStatus,
    session_key: String,
) -> Result<SessionListEntry> {
    let session_prefix = session_storage_prefix(status, &session_key);
    let updated_at_key = format!("{session_prefix}:updated-at");
    let updated_at = match handle
        .get_value(&updated_at_key)
        .with_context(|| format!("failed to read GitMeta key '{updated_at_key}'"))?
    {
        None => bail!("existing session '{session_key}' is missing updated-at"),
        Some(MetaValue::String(updated_at)) => updated_at,
        Some(_) => bail!("existing GitMeta key '{updated_at_key}' is not a string"),
    };
    let sort_updated_at = DateTime::parse_from_rfc3339(&updated_at)
        .with_context(|| format!("existing GitMeta key '{updated_at_key}' has invalid timestamp"))?
        .with_timezone(&Utc);
    Ok(SessionListEntry {
        session_key,
        status,
        updated_at,
        sort_updated_at,
    })
}

pub(crate) fn find_related_sessions_limited(
    repo_path: &Path,
    target: RelatedTarget<'_>,
    max_sessions: Option<usize>,
) -> Result<Vec<RelatedSession>> {
    find_related_sessions_limited_by_statuses(

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Run `but agentlog sync` to pull a consistent metadata state and retry the command
  2. Identify which writer version produced the key (inspect the GitMeta ref's git history) and align all machines on one `but` version, then re-publish the session
  3. Delete or rewrite the single malformed `gitbutler:agent-session:<status>:<key>:updated-at` key so the session either heals on next capture or is skipped
  4. If the session is dispensable, remove all of its `gitbutler:agent-session:<key>:*` keys so listing no longer trips over it

Example fix

// before: corrupt key breaks the entire listing
let entry = session_list_entry(&handle, status, session_key)?;

// after: skip sessions whose updated-at has the wrong shape
let entry = match session_list_entry(&handle, status, session_key) {
    Ok(entry) => entry,
    Err(err) if err.to_string().contains("is not a string") => continue,
    Err(err) => return Err(err),
};
Defensive patterns

Strategy: type-guard

Validate before calling

// Before listing, verify the updated-at key's variant
let key = format!("gitbutler:agent-session:{status}:{session_key}:updated-at");
match handle.get_value(&key)? {
    Some(MetaValue::String(_)) => { /* safe to call the listing API */ }
    Some(_) => { /* skip session; wrong shape */ }
    None => { /* session not fully stored */ }
}

Type guard

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

Try / catch

match session_list_entry(&handle, status, key) {
    Ok(entry) => entries.push(entry),
    Err(err) if err.to_string().contains("is not a string") => {
        eprintln!("skipping session {key}: malformed updated-at");
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Any session-listing path (`but agentlog show <session>`, skim discovery, related-session lookups) iterating a session whose `:updated-at` key was written as a Set or List instead of a String — e.g. after version skew between two but-agentlog writers, or a foreign/manual write to the GitMeta ref.

Common situations: Upgrading `but` binaries that changed the value encoding; two machines syncing metadata from different versions; hand-edits or tooling writing raw GitMeta keys; a push-conflict resolution that re-imported older-format keys.

Related errors


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