janhq/jan · error · UpdateError

Failed to parse update response

Error message

Failed to parse update response: {0}

What it means

UpdateError::ParseError is returned when the updater receives a response but cannot deserialize it into the expected update-information structure. The server's payload is converted to a string and embedded in 'Failed to parse update response: <detail>'.

Solutions

  1. Read the embedded detail to see the exact deserialization failure and the raw body
  2. Fetch the endpoint manually (curl) to inspect what is actually returned
  3. Confirm the endpoint serves the Tauri updater JSON schema (version, notes, pub_date, platforms)
  4. Update the response struct fields to match any backend format changes

Example fix

// before
let info: UpdateInfo = serde_json::from_str(&body).map_err(|e| UpdateError::ParseError(e.to_string()))?;
// after
let info: UpdateInfo = serde_json::from_str(&body)
    .map_err(|e| UpdateError::ParseError(format!("{} :: body={}", e, &body[..body.len().min(200]))))?; // log body for diagnosis
Defensive patterns

Strategy: validation

Validate before calling

const res = await fetch(endpoint);
const ct = res.headers.get('content-type') || '';
if (!ct.includes('application/json')) throw new Error(`unexpected content-type: ${ct}`);

Type guard

function looksLikeUpdateInfo(x: unknown): x is { version: string; notes?: string; pub_date?: string } {
  return typeof x === 'object' && x !== null && 'version' in x && typeof (x as any).version === 'string';
}

Prevention

When it happens

Trigger: An update endpoint returns HTML (error page), truncated JSON, or JSON whose shape no longer matches the Tauri-compatible updater struct expected by the deserializer.

Common situations: Update server behind a misconfigured reverse proxy returning an HTML 404/502 page; backend changed the manifest format; CDN serving a captive-portal or maintenance page.

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 janhq/jan@7205d770c1 (2026-09-17). Data as JSON: /api/errors/c90117b2fe17da63. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/src/core/updater/custom_updater.rs:34

use thiserror::Error;

/// Secret key for HMAC signature
/// - In CI: Set JAN_SIGNING_KEY environment variable at build time
/// - In local dev: Falls back to a test key
pub(crate) const SECRET_KEY: &str = match option_env!("JAN_SIGNING_KEY") {
    Some(key) => key,
    None => "local-dev-test-key-not-for-production",
};

/// Timeout for HTTP requests
const REQUEST_TIMEOUT_SECS: u64 = 30;

#[derive(Debug, Error)]
pub enum UpdateError {
    #[error("HTTP request failed: {0}")]
    RequestFailed(#[from] reqwest::Error),

    #[error("Failed to parse update response: {0}")]
    ParseError(String),

    #[error("All endpoints failed")]
    AllEndpointsFailed,

    #[error("Invalid response from server: {0}")]
    InvalidResponse(String),

    #[error("No endpoints configured")]
    NoEndpointsConfigured,
}

/// Update information returned by the update check endpoint
/// Compatible with Tauri's updater format
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateInfo {
    pub version: String,
    #[serde(default)]

View on GitHub (pinned to 7205d770c1)