janhq/jan · warning · UpdateError

Invalid response from server: {0}

Error message

Invalid response from server: {0}

What it means

The InvalidResponse variant of UpdateError is returned when the update endpoint responds with a non-2xx HTTP status code. The error message includes both the status code and the response body text, so you can see the server's error detail. This differs from ParseError (2xx but bad body) and RequestFailed (no response at all).

Source

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

    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)]
    pub notes: Option<String>,
    #[serde(default)]
    pub pub_date: Option<String>,
    #[serde(default)]
    pub platforms: Option<serde_json::Value>,
    /// URL to download the update

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Read the status code and body text in the error message to diagnose the server-side issue.
  2. If 401/403, verify the JAN_SIGNING_KEY build-time env var matches the server.
  3. If 429, reduce update check frequency.
  4. If 5xx, wait and retry — the server is temporarily unavailable.

Example fix

# reproduce the request manually to see server response
curl -v -H 'Accept: application/json' \
  -H 'User-Agent: Jan/1.0.0 (linux; x86_64)' \
  https://apps.jan.ai/update-check
Defensive patterns

Strategy: try-catch

Validate before calling

// In Rust, pre-check endpoint health (simplified)
async fn probe_endpoint(url: &str) -> Result<u16, String> {
    let resp = reqwest::Client::builder()
        .timeout(Duration::from_secs(5))
        .build()
        .map_err(|e| e.to_string())?
        .get(url)
        .send()
        .await
        .map_err(|e| e.to_string())?;
    Ok(resp.status().as_u16())
}

Try / catch

Err(UpdateError::InvalidResponse(detail)) => {
    log::warn!("Update endpoint returned invalid response: {detail}");
    // If it contains '401' or '403', the HMAC key may be wrong
    // If it contains '5xx', the server is down — try fallback endpoints
    // If it contains '429', back off before retrying
}

Prevention

When it happens

Trigger: Server returns 404 (endpoint path changed). Server returns 401/403 (HMAC signature rejected or IP blocked). Server returns 500/502/503 (server error, gateway timeout, maintenance). Server returns 429 (rate limited). CDN returns a non-2xx cached error.

Common situations: Endpoint URL changed after an infrastructure migration. HMAC signing key mismatch between client and server (JAN_SIGNING_KEY). Rate limiting from too-frequent update checks. Server-side outage or maintenance window. Reverse proxy misconfiguration.

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/c9860d58307d83e1. Report an issue: GitHub.