tonhowtf/omniget · error · std::io::Error

e.to_string()

Error message

e.to_string()

What it means

In save_state, after resolving the path, the state is serialized with serde_json::to_string_pretty. Serialization failures (the AnonymousCookieState contains a value serde cannot serialize, e.g. a map with non-string keys or a NaN float) are converted into an io::Error of Kind::InvalidData whose message is just e.to_string(). This aborts writing the anonymous cookie state.

Solutions

  1. Inspect the inner serde error (the io error's message) to find which field failed to serialize.
  2. Validate state values before saving — replace NaN/Infinity numbers with defaults (0 or None as Option).
  3. Change offending fields to serializable types (Option<f64>, String keys in maps).
  4. Use #[serde(default)] on new fields so old-state compatibility is maintained.

Example fix

// before
expires_at: f64, // can hold NaN
// after
#[serde(default)]
expires_at: Option<f64>, // None instead of NaN; always serializes
Defensive patterns

Strategy: validation

Validate before calling

fn state_serializable(state: &AnonymousCookieState) -> bool {
    serde_json::to_string(state).is_ok()
}
// call before save_state
if !state_serializable(&state) { log::warn!("cookie state not serializable; skipping save"); }

Try / catch

match save_state(&state) {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
        log::warn!("cookie state serialization failed: {e}");
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling save_state (via bootstrap_anonymous) with an AnonymousCookieState whose fields fail serde_json serialization: unexpected non-string map keys, unserializable numeric values (NaN/Infinity floats from timestamps/tokens), or a struct field type change without updating serialization.

Common situations: A struct refactor added a field type serde_json can't handle; corrupted in-memory state built from bad server responses (NaN expiry); version drift where old cached structs no longer match the serde derive.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/282fd760de8714cf. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/platforms/bilibili/cookie.rs:70

        Some(p) => p,
        None => return AnonymousCookieState::default(),
    };
    let content = match std::fs::read_to_string(&path) {
        Ok(c) => c,
        Err(_) => return AnonymousCookieState::default(),
    };
    serde_json::from_str(&content).unwrap_or_default()
}

pub fn save_state(state: &AnonymousCookieState) -> std::io::Result<()> {
    let path = meta_file_path().ok_or_else(|| {
        std::io::Error::new(std::io::ErrorKind::Other, "app_data_dir unavailable")
    })?;
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent)?;
    }
    let serialized = serde_json::to_string_pretty(state)
        .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?;
    std::fs::write(path, serialized)
}

pub async fn ensure_fresh() -> Result<AnonymousCookieState> {
    let cached = load_state();
    if cached.is_fresh() {
        write_netscape_for_anonymous(&cached).ok();
        return Ok(cached);
    }
    bootstrap_anonymous().await
}

pub async fn bootstrap_anonymous() -> Result<AnonymousCookieState> {
    let ua = super::api::DEFAULT_USER_AGENT;
    let now_ms = now_ms();
    let uuid = gen_uuid_b_lsid();
    let b_lsid = gen_b_lsid();
    let b_nut = now_ms.to_string();

View on GitHub (pinned to 8600b91f42)