{"record":{"id":"c9860d58307d83e1","repo":"janhq/jan","slug":"invalid-response-from-server-0","errorCode":null,"errorMessage":"Invalid response from server: {0}","messagePattern":"Invalid response from server: (.+?)","errorType":"error_code","errorClass":"UpdateError","httpStatus":null,"severity":"error","filePath":"src-tauri/src/core/updater/custom_updater.rs","lineNumber":40,"sourceCode":"    Some(key) => key,\n    None => \"local-dev-test-key-not-for-production\",\n};\n\n/// Timeout for HTTP requests\nconst REQUEST_TIMEOUT_SECS: u64 = 30;\n\n#[derive(Debug, Error)]\npub enum UpdateError {\n    #[error(\"HTTP request failed: {0}\")]\n    RequestFailed(#[from] reqwest::Error),\n\n    #[error(\"Failed to parse update response: {0}\")]\n    ParseError(String),\n\n    #[error(\"All endpoints failed\")]\n    AllEndpointsFailed,\n\n    #[error(\"Invalid response from server: {0}\")]\n    InvalidResponse(String),\n\n    #[error(\"No endpoints configured\")]\n    NoEndpointsConfigured,\n}\n\n/// Update information returned by the update check endpoint\n/// Compatible with Tauri's updater format\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct UpdateInfo {\n    pub version: String,\n    #[serde(default)]\n    pub notes: Option<String>,\n    #[serde(default)]\n    pub pub_date: Option<String>,\n    #[serde(default)]\n    pub platforms: Option<serde_json::Value>,\n    /// URL to download the update","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/janhq/jan/blob/7205d770c1e097c3daf35a911176410e93bc5564/src-tauri/src/core/updater/custom_updater.rs#L22-L58","documentation":"UpdateError::InvalidResponse is returned when the endpoint answered successfully but the response content is semantically invalid — for example an unexpected HTTP body, missing fields, or a status/shape the updater doesn't accept. It renders 'Invalid response from server: <detail>'.","triggerScenarios":"The updater receives a 200 response whose content fails its post-parse validation checks (e.g. missing version field or wrong content type), and the code constructs InvalidResponse(detail).","commonSituations":"Server updated its manifest schema; endpoint returns 200 with an HTML login page; load balancer intercepting requests; version string format changed.","solutions":["Read the embedded detail to identify which part of the response was invalid","Fetch the endpoint manually and compare against the expected updater format","Coordinate with the update-server team on schema changes","Pin/upgrade the app's updater to a version compatible with the new server format"],"exampleFix":"// before\nif info.version.is_empty() { return Err(UpdateError::InvalidResponse(\"missing version\".into())); }\n// after\nmatch semver::Version::parse(&info.version) {\n    Ok(_) => {}\n    Err(e) => return Err(UpdateError::InvalidResponse(format!(\"bad version '{}': {}\", info.version, e))),\n}","handlingStrategy":"validation","validationCode":"const body = await res.json();\nif (!body.version || !/^\\d+\\.\\d+\\.\\d+/.test(body.version)) throw new Error('invalid manifest: bad version');","typeGuard":"function isValidUpdateManifest(x: unknown): x is { version: string; pub_date: string; platforms: Record<string, unknown> } {\n  const m = x as any;\n  return !!m && typeof m.version === 'string' && typeof m.pub_date === 'string' && !!m.platforms;\n}","tryCatchPattern":null,"preventionTips":["Validate every required manifest field after parsing","Check the response is not an HTML error page despite a 200 status","Keep server and client manifest schemas in sync (contract tests)","Use strict deserialization (deny_unknown_fields or explicit checks) to catch drift early"],"tags":["http","updater","response-validation","rust"],"backgroundTag":"unexpected-response-shape","analyzedSha":"7205d770c1e097c3daf35a911176410e93bc5564","analyzedAt":"2026-09-17T14:27:30.100Z","contentChangedAt":"2026-09-17T14:27:30.100Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}