gitbutlerapp/gitbutler · error

existing GitMeta key

Error message

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

What it means

session_associations_observe_target reads the session's associated-targets key, expected to be a MetaValue::String containing JSON (StoredSessionAssociations). If the key exists but holds another MetaValue variant, the stored shape is invalid and the function bails. An absent key is fine (returns false); only a present-but-wrongly-typed value triggers this error.

Solutions

  1. Delete the invalid key and re-record the session associations (writer serializes StoredSessionAssociations as JSON)
  2. Fix the writer path to always store serde_json::to_string(&StoredSessionAssociations) as a string
  3. Check which tool/version wrote the key and align reader/writer schemas
  4. Rebuild the GitMeta data for the session if more keys are misshapen

Example fix

// before
handle.set_value(&associated_targets_key, MetaValue::List(entries));
// after
let json = serde_json::to_string(&StoredSessionAssociations::from(entries))?;
handle.set_value(&associated_targets_key, MetaValue::String(json));
Defensive patterns

Strategy: validation

Validate before calling

let v = gitmeta_get(&repo_path, &associated_targets_key);
let ok = matches!(v, None | Some(MetaValue::String(s)) if serde_json::from_str::<StoredSessionAssociations>(&s).is_ok());
if !ok { /* repair before reading associations */ }

Type guard

fn is_valid_associations(v: Option<&MetaValue>) -> bool {
    match v {
        None => true,
        Some(MetaValue::String(s)) => serde_json::from_str::<StoredSessionAssociations>(s).is_ok(),
        _ => false,
    }
}

Try / catch

match session_associations_observe_target(handle, target) {
    Ok(observes) => observes,
    Err(e) if e.to_string().contains("is not a string") => {
        handle.delete_value(&associated_targets_key);
        false
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: turn_detail_observes_target -> session_associations_observe_target when handle.get_value(associated_targets_key) returns Some(value) that is not MetaValue::String — the associations key was overwritten with a non-string value.

Common situations: Writer bug storing a set/list of targets instead of the JSON string; older or newer tool versions with different storage schemas sharing the repo; manual GitMeta edits; corrupted storage.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/10e60c56e1891ad2. Report an issue: GitHub.

Appendix: source

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

fn session_associations_observe_target(
    handle: &SessionTargetHandle<'_>,
    session_key: &str,
    status: PublicationStatus,
    target: RelatedTarget<'_>,
) -> Result<bool> {
    let associated_targets_key = format!(
        "{}:associated-targets",
        session_storage_prefix(status, session_key)
    );
    let Some(value) = handle
        .get_value(&associated_targets_key)
        .with_context(|| format!("failed to read GitMeta key '{associated_targets_key}'"))?
    else {
        return Ok(false);
    };
    let MetaValue::String(value) = value else {
        bail!("existing GitMeta key '{associated_targets_key}' is not a string");
    };
    let targets: StoredSessionAssociations = serde_json::from_str(&value).with_context(|| {
        format!("existing GitMeta key '{associated_targets_key}' has invalid JSON")
    })?;
    Ok(targets.observes(target))
}

#[derive(Deserialize)]
struct StoredSessionAssociations {
    #[serde(default)]
    branches: BTreeSet<String>,
    #[serde(default)]
    reviews: BTreeSet<String>,
    #[serde(default)]
    changes: BTreeSet<String>,
}

impl StoredSessionAssociations {

View on GitHub (pinned to 58e5313667)