cross-rs/cross · error · eyre::ErrReport

failed to serialize CrossToml as object

Error message

failed to serialize CrossToml as object

What it means

`CrossToml::to_map` serializes the TOML config into a `serde_json::Value` and expects it to be a JSON object (map). If the serialized value is not an object (e.g. an array or scalar), it fails, because the merge machinery (`merge`) can only combine map-shaped configs.

Solutions

  1. Ensure the cross.toml top level is a TOML table (key/value sections), not an array or bare value.
  2. If constructing CrossToml programmatically, build it as a map/struct that serializes to an object.
  3. Inspect the chained error ("could not convert CrossToml to serde_json::Value") for the underlying serialization failure.

Example fix

// before (invalid top-level TOML)
[[config]]
key = "value"
// after
[config]
key = "value"
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure top-level cross config is a table
if !raw_toml.trim_start().starts_with('[') && raw_toml.contains('=') && !raw_toml.contains('\n[') { /* likely scalar top-level, reject */ }

Try / catch

match CrossToml::merge(...) {
    Ok(t) => t,
    Err(e) => { eprintln!("config merge failed: {e:?>}"); return Err(e); }
}

Prevention

When it happens

Trigger: Calling `merge` on a `CrossToml` whose serialization does not yield a JSON object — typically an internal invariant violation when the TOML deserialized into a non-table top-level shape.

Common situations: A cross.toml whose top level is not a table (rare, usually only via programmatic construction or corrupted config), or a bug in the merge pipeline passing a non-map value.

Understand the failure class

Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of cross-rs/cross@8c1a8aa4b6 (2026-09-13). Data as JSON: /api/errors/2a75544e366a8156. Report an issue: GitHub.

Appendix: source

Thrown at src/cross_toml.rs:220

                unused.clone().into_iter().collect::<Vec<_>>().join(", ")
            ))?;
        }

        Ok((cfg, unused))
    }

    /// Merges another [`CrossToml`] into `self` and returns a new merged one
    pub fn merge(self, other: CrossToml) -> Result<CrossToml> {
        type ValueMap = serde_json::Map<String, serde_json::Value>;

        fn to_map<S: Serialize>(s: S) -> Result<ValueMap> {
            if let Some(obj) = serde_json::to_value(s)
                .wrap_err("could not convert CrossToml to serde_json::Value")?
                .as_object()
            {
                Ok(obj.clone())
            } else {
                eyre::bail!("failed to serialize CrossToml as object");
            }
        }

        fn from_map<D: DeserializeOwned>(map: ValueMap) -> Result<D> {
            let value = serde_json::to_value(map)
                .wrap_err("could not convert ValueMap to serde_json::Value")?;
            serde_json::from_value(value)
                .wrap_err("could not deserialize serde_json::Value to CrossToml")
        }

        // merge 2 objects. y has precedence over x.
        fn merge_objects(x: &mut ValueMap, y: &ValueMap) -> Option<()> {
            // we need to iterate over both keys, so we need a full deduplication
            let keys: BTreeSet<String> = x.keys().chain(y.keys()).cloned().collect();
            for key in keys {
                let in_x = x.contains_key(&key);
                let in_y = y.contains_key(&key);
                if !in_x && in_y {

View on GitHub (pinned to 8c1a8aa4b6)