unicity-aos/capsule-identity · warning

Failed to parse spark.toml, using defaults

Error message

Failed to parse spark.toml, using defaults: {e}

What it means

parse_spark_toml parses spark.toml content into a SparkConfig via toml::from_str. When parsing fails, it logs this warning and falls back to SparkConfig::default() instead of propagating the error, so the caller (handle_command) always receives a valid config. The interpolated `e` carries the underlying serde/TOML error detailing why deserialization failed.

Solutions

  1. Fix the TOML syntax error at the location given in the parse error `e` inside the logged message.
  2. Check that all fields conform to SparkConfig's serde types and remove obsolete or mistyped fields.
  3. Replace the file with SparkConfig::default() output (serialize via toml::to_string) and re-add settings one at a time, re-running to confirm each parses.
  4. For callers needing the exact problem surfaced, consider changing parse_spark_toml to return Result<SparkConfig, toml::de::Error> rather than silently defaulting, or at least surface the warning in command output.

Example fix

// before: silent fallback
toml::from_str(content).unwrap_or_else(|e| {
    log::warn(format!("Failed to parse spark.toml, using defaults: {e}"));
    SparkConfig::default()
})

// after: caller can see and handle the failure
match toml::from_str::<SparkConfig>(content) {
    Ok(cfg) => cfg,
    Err(e) => {
        log::warn(format!("Failed to parse spark.toml, using defaults: {e}"));
        SparkConfig::default()
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate before passing content to the parser
fn validate_spark_toml(content: &str) -> Result<SparkConfig, toml::de::Error> {
    toml::from_str(content)
}
// caller:
let cfg = validate_spark_toml(&content)?; // or handle Err explicitly

Type guard

fn parses_as_spark_config(content: &str) -> bool {
    toml::from_str::<SparkConfig>(content).is_ok()
}

Try / catch

// Handle the Err branch explicitly instead of trusting the silent default
let config = toml::from_str::<SparkConfig>(content)
    .unwrap_or_else(|e| {
        log::warn(format!("spark.toml invalid ({e}); defaults in effect"));
        SparkConfig::default()
    });

Prevention

When it happens

Trigger: Calling handle_command with a command that routes through parse_spark_toml on spark.toml content that toml::from_str cannot deserialize: invalid TOML syntax, wrong field types relative to SparkConfig, or incompatible/unknown field structure for the current schema.

Common situations: Hand-edited spark.toml with syntax mistakes; config written by an older release whose fields were renamed or retyped; a partially written/corrupted file (e.g. crash mid-write); copy-pasting config with mismatched quotes or indentation in TOML.

Understand the failure class

Related errors


AI-assisted analysis of unicity-aos/capsule-identity@1364a437f3 (2026-09-13). Data as JSON: /api/errors/2eed509fd2bb5673. Report an issue: GitHub.

Appendix: source

Thrown at src/lib.rs:279

    pub fn save_identity(&mut self, args: SparkConfig) -> Result<serde_json::Value, SysError> {
        self.spark = args;
        self.onboarded = true;

        // Persist to spark.toml so identity survives KV resets.
        let toml = self.spark.to_toml();
        fs::write(SPARK_CONFIG_PATH, toml.as_bytes())?;

        Ok(serde_json::json!({
            "status": "ok",
            "callsign": self.spark.callsign,
        }))
    }
}

/// Parse spark.toml into a `SparkConfig`.
fn parse_spark_toml(content: &str) -> SparkConfig {
    toml::from_str(content).unwrap_or_else(|e| {
        log::warn(format!("Failed to parse spark.toml, using defaults: {e}"));
        SparkConfig::default()
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    fn configured_identity() -> SparkConfig {
        SparkConfig {
            callsign: "Lyra".into(),
            class: "a precise concierge agent".into(),
            aura: "Calm, direct, and context aware.".into(),
            signal: "Use short answers unless detail is needed.".into(),
            core: "Preserve user boundaries.".into(),
        }
    }

View on GitHub (pinned to 1364a437f3)