neondatabase/neon · critical
could not parse spec
Error message
could not parse spec
What it means
A ComputeSpec arrived and ParsedSpec::try_from(spec) failed, so the configurator replaces it with the generic 'could not parse spec'. The real cause is one of the required-field/parse errors inside try_from (missing pageserver connstring, safekeepers, tenant/timeline id, or a malformed UUID). Note the code uses 'if let Ok', which discards the underlying anyhow error - the single biggest debugging obstacle here.
Source
Thrown at compute_tools/src/configurator.rs:107
Err(anyhow::anyhow!(
"could not open config file at path: {}",
config_path.to_string_lossy()
))
}
} else if let Some(control_plane_uri) = &compute.params.control_plane_uri {
get_config_from_control_plane(control_plane_uri, &compute.params.compute_id)
} else {
Err(anyhow::anyhow!("config_path_test_only is not set"))
};
// Parse any received ComputeSpec and transpose the result into a Result<Option<ParsedSpec>>.
let parsed_spec_result: Result<Option<ParsedSpec>> =
get_config_result.and_then(|config| {
if let Some(spec) = config.spec {
if let Ok(pspec) = ParsedSpec::try_from(spec) {
Ok(Some(pspec))
} else {
Err(anyhow::anyhow!("could not parse spec"))
}
} else {
Ok(None)
}
});
let new_status: ComputeStatus;
match parsed_spec_result {
// Control plane (HCM) returned a spec and we were able to parse it.
Ok(Some(pspec)) => {
{
let mut state = compute.state.lock().unwrap();
// Defensive programming to make sure this thread is indeed the only one that can move the compute
// node out of the `RefreshConfiguration` state. Would be nice if we can encode this invariant
// into the type system.
assert_eq!(state.status, ComputeStatus::RefreshConfiguration);
if stateView on GitHub (pinned to 8f60b04da4)
Solutions
- Propagate the real error: replace 'if let Ok(pspec)' with a match that chains {:#} of the underlying error - the message then tells you which field is the culprit
- Independently run the spec through ParsedSpec::try_from (unit test / small binary) to see the actual failure
- Fix the spec per the revealed message: add the missing GUCs/fields or correct malformed UUIDs
- Check for control-plane vs compute_ctl version skew and align them
Example fix
// before
if let Ok(pspec) = ParsedSpec::try_from(spec) { Ok(Some(pspec)) } else { Err(anyhow!("could not parse spec")) }
// after
match ParsedSpec::try_from(spec) {
Ok(pspec) => Ok(Some(pspec)),
Err(e) => Err(anyhow!("could not parse spec: {e:#}")),
} Defensive patterns
Strategy: validation
Validate before calling
// Reuse the parser as a validator: run try_from and keep the real error
let probe = ParsedSpec::try_from(spec.clone());
if let Err(e) = probe { anyhow::bail!("spec will fail to parse: {e:#}"); } Type guard
fn is_parseable_spec(spec: &ComputeSpec) -> bool {
ParsedSpec::try_from(spec.clone()).is_ok()
} Try / catch
// Propagate the source error chain instead of the generic message
match ParsedSpec::try_from(spec) {
Ok(pspec) => Ok(Some(pspec)),
Err(e) => Err(anyhow!("could not parse spec: {e:#}")),
} Prevention
- Never swallow the inner error: chain it with {:#} so errors 3-6 are visible directly
- Contract-test control-plane spec generation against ParsedSpec::try_from on every release
- For local specs, validate required GUCs (pageserver_connstring, safekeepers, tenant_id, timeline_id) before launch
When it happens
Trigger: get_config_result carries a config whose spec fails ParsedSpec::try_from: any of errors 3-6 (missing neon.pageserver_connstring / neon.safekeepers / neon.tenant_id / neon.timeline_id) or 'invalid tenant id' / 'invalid timeline id' formatting, or later try_from invariants.
Common situations: Control plane and compute_ctl version skew producing incomplete specs; new validation added to try_from rejecting previously-accepted specs; hand-crafted specs for local testing; JSON field renames between versions.
Related errors
- pageserver connection information should be provided
- safekeeper connstrings should be provided
- tenant id should be provided
- timeline id should be provided
- Remote extensions are not configured
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/44c5038bafe622c2.
Report an issue: GitHub.