{"record":{"id":"fad74aa46bd80bdc","repo":"gitbutlerapp/gitbutler","slug":"existing-gitmeta-key-index-key-is-not-a-set","errorCode":null,"errorMessage":"existing GitMeta key '{index_key}' is not a set","messagePattern":"existing GitMeta key '(.+?)' is not a set","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-agentlog/src/gitmeta/read.rs","lineNumber":205,"sourceCode":"            }\n        })\n        .collect())\n}\n\nfn target_index_hits(\n    handle: &SessionTargetHandle<'_>,\n    target: RelatedTarget<'_>,\n    status: PublicationStatus,\n) -> Result<BTreeSet<String>> {\n    let index_key = status_index_key(status, target.index_kind(), target.key());\n    let Some(value) = handle\n        .get_value(&index_key)\n        .with_context(|| format!(\"failed to read GitMeta key '{index_key}'\"))?\n    else {\n        return Ok(BTreeSet::new());\n    };\n    let MetaValue::Set(index_hits) = value else {\n        bail!(\"existing GitMeta key '{index_key}' is not a set\");\n    };\n    Ok(index_hits)\n}\n\nfn turn_detail_observes_target(\n    handle: &SessionTargetHandle<'_>,\n    hit: &IndexHit,\n    target: RelatedTarget<'_>,\n    status: PublicationStatus,\n    session_activity_matches: &mut BTreeMap<String, bool>,\n) -> Result<bool> {\n    let detail_key = format!(\n        \"{}:turn:{}\",\n        session_storage_prefix(status, &hit.session_key),\n        hit.turn_key\n    );\n    let Some(detail) = read_optional_turn_detail(handle, &detail_key)? else {\n        return Ok(false);","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-agentlog/src/gitmeta/read.rs#L187-L223","documentation":"but-agentlog keeps per-status index keys (built by `status_index_key`, one per branch/review/change target) whose value must be a `MetaValue::Set` of JSON-serialized `IndexHit` entries pointing at sessions. When reading the index for a related target, a value of any other MetaValue variant triggers this bail. It signals the index key exists but was stored with the wrong shape.","triggerScenarios":"`but agentlog skim branch <name>` / `skim review <id>` / `skim change <id>`, or `publish <target>`, reading `status_index_key(status, kind, key)` when that key holds a String or List instead of a Set of IndexHit JSON strings.","commonSituations":"Index key written by an older/newer but-agentlog with a different encoding; GitMeta push-conflict resolutions merging keys of mixed shapes; manual edits or third-party tools writing the index key; partial migration leaving a stale index value.","solutions":["Run `but agentlog sync` and retry the skim/publish — a clean pull often replaces the malformed index","Align all machines on one `but` version so one writer shape is used, then re-publish the target to rewrite the index key","Inspect the GitMeta ref history for the index key and restore the last version that was a Set","Delete the malformed index key (listings degrade to 'no related sessions' until re-published) rather than aborting every read"],"exampleFix":"// before\nlet hits = index_hits(&handle, target, status)?; // bails on wrong variant\n\n// after: treat a wrongly-shaped index as empty\nlet hits = match index_hits(&handle, target, status) {\n    Ok(hits) => hits,\n    Err(err) if err.to_string().contains(\"is not a set\") => BTreeSet::new(),\n    Err(err) => return Err(err),\n};","handlingStrategy":"type-guard","validationCode":"// Pre-check the index key's variant before skim/publish\nlet index_key = status_index_key(status, kind, target_key);\nif let Some(value) = handle.get_value(&index_key)? {\n    if !matches!(value, MetaValue::Set(_)) {\n        // malformed index; skip or repair before calling skim\n    }\n}","typeGuard":"fn index_is_set(v: &MetaValue) -> bool {\n    matches!(v, MetaValue::Set(_))\n}","tryCatchPattern":"match index_hits(&handle, target, status) {\n    Ok(hits) => process(hits),\n    Err(err) if err.to_string().contains(\"is not a set\") => {\n        eprintln!(\"index for target is malformed; treating as empty\")\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Re-publish a target after upgrading but-agentlog to rewrite its index keys","Avoid mixing but versions that sync the same metadata remote","Treat index keys as but-agentlog-owned; never write them directly"],"tags":["gitmeta","type-mismatch","index","skim","rust"],"backgroundTag":"stored-data-type-mismatch","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}