gitbutlerapp/gitbutler · error

existing GitMeta key '{local_key}' is not a set

Error message

existing GitMeta key '{local_key}' is not a set

What it means

Unpublished (local-only) sessions are tracked under a local index namespace as `MetaValue::Set` keys, each holding JSON `IndexHit` members. `share.rs` iterates all keys under that prefix via `get_all_values` and bails on the first key that is not a Set. Because it scans every key under the prefix, one malformed key disables the whole share/publish flow.

Source

Thrown at crates/but-agentlog/src/gitmeta/share.rs:79

    Ok(())
}

fn index_set_member_shares(
    handle: &git_meta_lib::SessionTargetHandle<'_>,
    session_keys: &[String],
) -> Result<Vec<SetMemberShare>> {
    let selected_sessions = session_keys
        .iter()
        .map(String::as_str)
        .collect::<BTreeSet<_>>();
    let index_prefix = local_storage_key(INDEX_NAMESPACE);
    let mut shares = BTreeMap::<String, SetMemberShare>::new();
    for (local_key, value) in handle
        .get_all_values(Some(&index_prefix))
        .with_context(|| format!("failed to read GitMeta keys under '{index_prefix}'"))?
    {
        let MetaValue::Set(members) = value else {
            bail!("existing GitMeta key '{local_key}' is not a set");
        };
        let Some(published_key) = strip_local_storage_prefix(&local_key) else {
            continue;
        };
        let published_key = published_key.to_owned();
        let selected_members = members
            .into_iter()
            .filter(|member| match serde_json::from_str::<IndexHit>(member) {
                Ok(hit) => selected_sessions.contains(hit.session_key.as_str()),
                Err(_) => false,
            })
            .collect::<Vec<_>>();
        if selected_members.is_empty() {
            continue;
        }
        shares.insert(
            local_key.clone(),
            SetMemberShare {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Run `but agentlog sync` then retry publish
  2. Locate the malformed key (list all keys under the local index prefix with their MetaValue variants) and delete or rewrite it
  3. Align `but` versions so only one local-index format is written
  4. If the local index is fully stale, remove the namespace's keys and let the next capture rebuild it

Example fix

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

// after: skip malformed local index keys instead of aborting the scan
let members = match value {
    MetaValue::Set(members) => members,
    _ => continue,
};
Defensive patterns

Strategy: validation

Validate before calling

// Pre-scan local index keys and report any non-Set members before publish
for (key, value) in handle.get_all_values(Some(&index_prefix))? {
    if !matches!(value, MetaValue::Set(_)) {
        eprintln!("malformed local index key: {key}");
    }
}

Type guard

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

Try / catch

let members = match value {
    MetaValue::Set(members) => members,
    _ => continue, // skip malformed local index keys
};

Prevention

When it happens

Trigger: `but agentlog publish <target>` (and dry-run) scanning local index keys when any `local_storage_key(INDEX_NAMESPACE)`-prefixed key holds a String or List instead of a Set of IndexHit JSON entries.

Common situations: Leftover keys from an older but-agentlog format under the same prefix; manual writes into the local index namespace; a partially-completed migration between local key layouts.

Related errors


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