{"record":{"id":"584a30a65cf35bba","repo":"gitbutlerapp/gitbutler","slug":"existing-gitmeta-key-associated-targets-key-is-584a30","errorCode":null,"errorMessage":"existing GitMeta key '{associated_targets_key}' is not a string","messagePattern":"existing GitMeta key '(.+?)' is not a string","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-agentlog/src/gitmeta/write.rs","lineNumber":510,"sourceCode":"                .iter()\n                .map(|key| status_index_key(status, \"change\", key)),\n        );\n        keys\n    }\n}\n\nfn session_target_associations(\n    handle: &SessionTargetHandle<'_>,\n    associated_targets_key: &str,\n) -> Result<TargetAssociations> {\n    let Some(value) = handle\n        .get_value(associated_targets_key)\n        .with_context(|| format!(\"failed to read GitMeta key '{associated_targets_key}'\"))?\n    else {\n        return Ok(TargetAssociations::default());\n    };\n    let MetaValue::String(value) = value else {\n        bail!(\"existing GitMeta key '{associated_targets_key}' is not a string\");\n    };\n    serde_json::from_str::<TargetAssociations>(&value).with_context(|| {\n        format!(\"existing GitMeta key '{associated_targets_key}' has invalid JSON\")\n    })\n}\n\nfn index_hits_for_turns(session_key: &str, turn_keys: &[String]) -> Result<Vec<String>> {\n    turn_keys\n        .iter()\n        .map(|turn_key| {\n            serde_json::to_string(&IndexHit {\n                session_key: session_key.to_owned(),\n                turn_key: turn_key.to_owned(),\n            })\n            .context(\"failed to serialize agentlog index hit\")\n        })\n        .collect()\n}","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-agentlog/src/gitmeta/write.rs#L492-L528","documentation":"During publish, `session_target_associations` reads the session's `:associated-targets` key to merge new branch/review/change observations. A missing key yields defaults, but an existing key that is not a `MetaValue::String` (holding the TargetAssociations JSON) bails. This blocks the write path, so publishing cannot update associations for that session.","triggerScenarios":"`but agentlog publish <target>` on a session whose existing `:associated-targets` key holds a Set or List variant — typically written by an incompatible but-agentlog version or reshaped by a conflicted metadata merge.","commonSituations":"Mixed `but` versions writing the same session; push-conflict resolutions producing wrong shapes; manual GitMeta edits; incompatible restores.","solutions":["Run `but agentlog sync` and retry publish","Re-capture the session under the current `but` build so the associations key is rewritten as JSON-in-a-String","Align versions across machines sharing the GitMeta remote","Restore the last String-valued key from GitMeta history or delete it (absence is treated as default associations)"],"exampleFix":"// before\nlet MetaValue::String(value) = value else { bail!(\"...\") };\n\n// after: treat a malformed associations value as empty\nlet associations = match value {\n    MetaValue::String(value) => serde_json::from_str::<TargetAssociations>(&value)?,\n    _ => TargetAssociations::default(),\n};","handlingStrategy":"validation","validationCode":"// Before publish, validate the associations key's shape\nif let Some(value) = handle.get_value(associated_targets_key)? {\n    if !matches!(value, MetaValue::String(_)) { /* malformed; sync or re-capture first */ }\n}","typeGuard":"fn associations_appendable(v: Option<&MetaValue>) -> bool {\n    matches!(v, None | Some(MetaValue::String(_)))\n}","tryCatchPattern":"match publish(&repo, &session) {\n    Ok(()) => {}\n    Err(err) if err.to_string().contains(\"associated-targets\") => {\n        eprintln!(\"associations key malformed; run sync, then re-publish\")\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Write associations only through but-agentlog publish/hook","Align but versions across writers","After conflicted syncs, re-publish affected sessions to normalize their keys"],"tags":["gitmeta","type-mismatch","associated-targets","publish","write-path","rust"],"backgroundTag":"stored-data-type-mismatch","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}