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
- Validate the source file with jq . file or python -m json.tool, fix the syntax error, and retry discovery
- Strip JSONC comments / trailing commas (convert to strict JSON) before importing
- 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
- Run jq . source.json on every file before importing
- Convert JSONC configs to strict JSON (strip comments/trailing commas) before discovery
- Verify the path points at the actual config, not a lockfile, log, or HTML page
- Regenerate exports from the source tool rather than hand-copying them
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
- A managed, project or plugin connector already uses this…
- .annotations is not a valid MCP annotations object
- .size must be an integer
- Failed to parse MCP config
- Failed to parse MCP config
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)