clockworklabs/SpacetimeDB · error
Invalid JSON5 string
Error message
Invalid JSON5 string
What it means
The environment config parser pre-scans quoted regions of a JSON5 file and decodes each quoted token with `json5::from_str`. If a quoted span is not a valid JSON5 string (e.g. an unterminated or malformed quote), parsing of the whole config fails with this generic error to avoid leaking secret-bearing diagnostics.
Solutions
- Check the config file for unterminated or improperly escaped quoted strings.
- Escape internal quotes (\" or \') and backslashes in values.
- Validate the JSON5 config with a local JSON5 parser before publishing.
Example fix
// before (config) PASSWORD: 'it's secret // after (config) PASSWORD: "it's secret"
Defensive patterns
Strategy: validation
Validate before calling
// validate quotes in the config before running the CLI
const json5 = require('json5');
try { json5.parse(fs.readFileSync(configPath, 'utf8')); } catch (e) { console.error('Malformed quoted string in config:', e.message); } Try / catch
match result { Err(e) if e.to_string() == "Invalid JSON5 string" => { eprintln!("Check unterminated quotes/escapes in config"); }, Err(e) => return Err(e), Ok(v) => v } Prevention
- Escape internal quotes and backslashes inside config values.
- Prefer double-quoted JSON strings for values containing apostrophes.
- Run a JSON5 validator locally before publishing secret-bearing configs.
When it happens
Trigger: A config file contains an unterminated or malformed quoted string, such as a missing closing quote or an invalid escape sequence inside a value.
Common situations: Hand-editing config with secrets; pasting values with unmatched quotes; apostrophes in passwords that clash with JSON5 single-quote delimiters.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse config file
- Invalid JSON5 configuration
- Environment key : numeric config input must use JSON number…
- Invalid numeric configuration input
- cannot replace without enclosing directory
AI-assisted analysis of clockworklabs/SpacetimeDB@eddf9f5014 (2026-09-20).
Data as JSON: /api/errors/5104d635557d69fd.
Report an issue: GitHub.
Appendix: source
Thrown at crates/cli/src/spacetime_config/environment.rs:71
{
i += content[i..].chars().next().unwrap().len_utf8();
}
if i == start {
i += content[i..].chars().next().unwrap().len_utf8();
}
let token = &content[start..i];
if (token.starts_with(|c: char| c.is_ascii_digit() || matches!(c, '-' | '+' | '.'))
|| matches!(token, "Infinity" | "NaN"))
&& !content[i..].trim_start_matches(is_space).starts_with(':')
{
replacements.push((start, i));
numbers.push(token);
continue;
}
}
}
if matches!(bytes[start], b'\'' | b'"') {
strings.push(json5::from_str(&content[start..i]).map_err(|_| anyhow::anyhow!("Invalid JSON5 string"))?);
}
}
// Check decoded strings as well: Unicode escapes must not manufacture an
// internal marker and cause a string to be interpreted as a number.
let mut prefix = "__spacetime_numeric_".to_owned();
while content.contains(&prefix) || strings.iter().any(|s| s.contains(&prefix)) {
prefix.push('_');
}
let mut text = String::with_capacity(content.len());
let mut previous = 0;
for (index, (start, end)) in replacements.into_iter().enumerate() {
text.push_str(&content[previous..start]);
text.push_str(&format!("\"{prefix}{index}\""));
previous = end;
}
text.push_str(&content[previous..]);
// Parser diagnostics may quote the source line, which can contain secrets.
let mut value: Value = json5::from_str(&text).map_err(|_| anyhow::anyhow!("Invalid JSON5 configuration"))?;View on GitHub (pinned to eddf9f5014)