clockworklabs/SpacetimeDB · error
Unable to parse invalid saved server fingerprint as ECDSA pu
Error message
Unable to parse invalid saved server fingerprint as ECDSA public key.
Update the server's fingerprint with:
spacetime server fingerprint {} What it means
The CLI's server_decoding_key (crates/cli/src/config.rs:759) loads the server fingerprint previously saved for that server and parses it with jsonwebtoken's DecodingKey::from_ec_pem, which requires a PEM-encoded ECDSA public key. If the saved string is corrupt, truncated, or in another format (raw hex, base64 body without PEM armor), parsing fails and the error tells you how to refresh it with `spacetime server fingerprint <server>` (the server name, or empty for the default, is interpolated).
Source
Thrown at crates/cli/src/config.rs:759
// (see https://github.com/clockworklabs/SpacetimeDB/pull/1341#issuecomment-2150857432).
if let Err(e) = atomic_write(&home_path.0, config) {
eprintln!("Could not save config file: {e}")
}
}
pub fn server_decoding_key(&self, server: Option<&str>) -> anyhow::Result<DecodingKey> {
self.server_fingerprint(server).and_then(|fing| {
if let Some(fing) = fing {
DecodingKey::from_ec_pem(fing.as_bytes()).with_context(|| {
format!(
"Unable to parse invalid saved server fingerprint as ECDSA public key.
Update the server's fingerprint with:
\tspacetime server fingerprint {}",
server.unwrap_or("")
)
})
} else {
Err(anyhow::anyhow!(
"No fingerprint saved for server: {}",
self.server_nick_or_host(server)?,
))
}
})
}
pub fn server_nick_or_host<'a>(&'a self, server: Option<&'a str>) -> anyhow::Result<&'a str> {
if let Some(server) = server {
let (host, _) = host_or_url_to_host_and_protocol(server);
Ok(host)
} else {
self.home.default_server().map(ServerConfig::nick_or_host)
}
}
pub fn server_fingerprint(&self, server: Option<&str>) -> anyhow::Result<Option<&str>> {
if let Some(server) = server {View on GitHub (pinned to 6dee26c6ef)
Solutions
- Re-record the fingerprint from the live server: `spacetime server fingerprint <host-or-nickname>` (or without an argument for the default server).
- If re-fetching is not possible, remove the server's fingerprint entry from the CLI config so the next connection re-establishes it.
- Check the stored value starts with `-----BEGIN PUBLIC KEY-----` and ends with the matching footer on its own line - multi-line PEM must survive whatever transport stored it.
Example fix
# before: saved fingerprint is corrupt / non-PEM spacetime login # Unable to parse invalid saved server fingerprint as ECDSA public key... # after: refresh from the server spacetime server fingerprint mainnet spacetime login
Defensive patterns
Strategy: fallback
Validate before calling
// Preflight: sanity-check the stored PEM before use.
let fp = config.server_fingerprint(server).ok().flatten().unwrap_or_default();
if !fp.is_empty() && !(fp.trim_start().starts_with("-----BEGIN") && fp.contains("-----END")) {
anyhow::bail!("saved fingerprint is not PEM; run: spacetime server fingerprint {}", server.unwrap_or(""));
} Try / catch
# On failure, refresh the fingerprint and retry once:
spacetime login || { spacetime server fingerprint "$SERVER" && spacetime login; } Prevention
- Refresh fingerprints with `spacetime server fingerprint <server>` right after server key rotations.
- Never hand-edit or single-line the PEM block in the CLI config; keep it byte-exact multiline.
- When templating configs across machines, base64 the whole config file rather than inlining the PEM into other formats.
When it happens
Trigger: Running any CLI command that verifies server JWTs (login/token flows) against a server whose saved fingerprint entry in the CLI config is not valid EC PEM; hand-editing the config and breaking the PEM block; a partial write or an older tool version that stored a different fingerprint format.
Common situations: Config files synced/templated across machines mangling the multiline PEM; downgrading/upgrading CLI versions with changed fingerprint storage; pasting a fingerprint from docs as raw hex instead of fetching it.
Understand the failure class
- 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 {}: {}
- Multiple databases found in config: {}. Please specify which
- Database '{}' is not in the config file. If you want to run
- could not serialize result: object had neither a `ok` nor an
- Tried to read ${n} byte(s) at relative offset ${this.offset}
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/d9e799540035147d.
Report an issue: GitHub.