gitbutlerapp/gitbutler · error
existing GitMeta key '{associated_targets_key}' is not a str
Error message
existing GitMeta key '{associated_targets_key}' is not a string What it means
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.
Source
Thrown at crates/but-agentlog/src/gitmeta/write.rs:510
.iter()
.map(|key| status_index_key(status, "change", key)),
);
keys
}
}
fn session_target_associations(
handle: &SessionTargetHandle<'_>,
associated_targets_key: &str,
) -> Result<TargetAssociations> {
let Some(value) = handle
.get_value(associated_targets_key)
.with_context(|| format!("failed to read GitMeta key '{associated_targets_key}'"))?
else {
return Ok(TargetAssociations::default());
};
let MetaValue::String(value) = value else {
bail!("existing GitMeta key '{associated_targets_key}' is not a string");
};
serde_json::from_str::<TargetAssociations>(&value).with_context(|| {
format!("existing GitMeta key '{associated_targets_key}' has invalid JSON")
})
}
fn index_hits_for_turns(session_key: &str, turn_keys: &[String]) -> Result<Vec<String>> {
turn_keys
.iter()
.map(|turn_key| {
serde_json::to_string(&IndexHit {
session_key: session_key.to_owned(),
turn_key: turn_key.to_owned(),
})
.context("failed to serialize agentlog index hit")
})
.collect()
}View on GitHub (pinned to caf1f223d3)
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)
Example fix
// before
let MetaValue::String(value) = value else { bail!("...") };
// after: treat a malformed associations value as empty
let associations = match value {
MetaValue::String(value) => serde_json::from_str::<TargetAssociations>(&value)?,
_ => TargetAssociations::default(),
}; Defensive patterns
Strategy: validation
Validate before calling
// Before publish, validate the associations key's shape
if let Some(value) = handle.get_value(associated_targets_key)? {
if !matches!(value, MetaValue::String(_)) { /* malformed; sync or re-capture first */ }
} Type guard
fn associations_appendable(v: Option<&MetaValue>) -> bool {
matches!(v, None | Some(MetaValue::String(_)))
} Try / catch
match publish(&repo, &session) {
Ok(()) => {}
Err(err) if err.to_string().contains("associated-targets") => {
eprintln!("associations key malformed; run sync, then re-publish")
}
Err(err) => return Err(err),
} Prevention
- Write associations only through but-agentlog publish/hook
- Align but versions across writers
- After conflicted syncs, re-publish affected sessions to normalize their keys
When it happens
Trigger: `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.
Common situations: Mixed `but` versions writing the same session; push-conflict resolutions producing wrong shapes; manual GitMeta edits; incompatible restores.
Related errors
- existing GitMeta key '{associated_targets_key}' is not a str
- existing GitMeta key '{local_key}' is not a set
- existing GitMeta key '{turns_key}' is not a list
- existing GitMeta key '{record_hashes_key}' is not a set
- existing GitMeta key '{detail_key}' is not a string
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/584a30a65cf35bba.
Report an issue: GitHub.