Hmbown/CodeWhale · error · anyhow::Error

Source is not valid JSON; contents omitted

Error message

Source is not valid JSON; contents omitted

What it means

checked_source in crates/tui/src/mcp/external_import.rs reads a candidate external MCP source file and parses it as JSON before extracting servers. If serde_json::from_str fails, the file is not valid JSON and discovery aborts. The message omits contents on purpose — imported files may contain secrets.

Solutions

  1. Validate the source file with jq . file or python -m json.tool, fix the syntax error, and retry discovery
  2. Strip JSONC comments / trailing commas (convert to strict JSON) before importing
  3. Point discovery at the correct file — verify the path is the actual config, not a log, lockfile, or downloaded page

Example fix

// before (source file: JSONC with comment — invalid JSON)
{ // my servers
  "mcpServers": { "fs": { "command": "npx" } } }
// after
{ "mcpServers": { "fs": { "command": "npx" } } }
Defensive patterns

Strategy: validation

Validate before calling

fn source_is_valid_json(path: &std::path::Path) -> bool {
    std::fs::read_to_string(path).ok()
        .map(|s| serde_json::from_str::<serde_json::Value>(&s).is_ok())
        .unwrap_or(false)
}

Type guard

fn parse_json(text: &str) -> Option<serde_json::Value> {
    serde_json::from_str(text).ok()
}

Try / catch

match discover(&path) {
    Err(e) if e.to_string().contains("not valid JSON") => {
        eprintln!("source file is not strict JSON; strip comments/trailing commas first");
    }
    r => r?,
}

Prevention

When it happens

Trigger: discover(path) or discover_from_json_file(path) pointing at a file that exists and is readable but whose text is not valid JSON (trailing commas, comments, JSONC, TOML, HTML error page).

Common situations: Importing another tool's JSONC-with-comments config; pointing at a config.json that is actually truncated; accidentally selecting a lockfile or an HTML download instead of the config.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/c77a3596b6caa6fe. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/mcp/external_import.rs:135

    }
    out
}

fn discover_from_json_file(path: &Path, kind: ExternalMcpSourceKind) -> Vec<ImportCandidate> {
    checked_source(path, kind).unwrap_or_default()
}

fn checked_source(
    path: &Path,
    kind: ExternalMcpSourceKind,
) -> anyhow::Result<Vec<ImportCandidate>> {
    super::validate_mcp_config_path(path)?;
    let Some(raw) = super::read_mcp_config_file(path)? else {
        return Ok(Vec::new());
    };
    let hash = hex_sha256(raw.as_bytes());
    let value: Value = serde_json::from_str(&raw)
        .map_err(|_| anyhow::anyhow!("Source is not valid JSON; contents omitted"))?;
    anyhow::ensure!(
        value
            .get("mcpServers")
            .or_else(|| value.get("servers"))
            .is_some_and(Value::is_object)
            || value.is_array(),
        "Source has no supported MCP server map"
    );
    let mut out = Vec::new();
    for (name, mut config) in extract_servers_map(&value) {
        anyhow::ensure!(
            super::mcp_name_is_command_safe(&name) && name.len() <= 128,
            "Source contains an unsupported server name"
        );
        if value.is_array()
            && let Some(map) = config.as_object_mut()
        {
            map.remove("name");

View on GitHub (pinned to 73e0f67d83)