{"record":{"id":"385eb72c06f56212","repo":"eyaltoledano/claude-task-master","slug":"api-error-385eb7","errorCode":"API_ERROR","errorMessage":"API returned non-JSON response (${response.status}): ${text.substring(0, 100)}...","messagePattern":"API returned non-JSON response \\((.+?)\\): (.+?)\\.\\.\\.","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/tm-core/src/modules/integration/services/export.service.ts","lineNumber":1108,"sourceCode":"\t\ttry {\n\t\t\tconst response = await fetch(apiUrl, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: {\n\t\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\t\t'x-account-id': accountId // Also send as header for redundancy\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(requestBody)\n\t\t\t});\n\n\t\t\t// Check content type to avoid JSON parse errors on HTML responses (e.g., 404 pages)\n\t\t\tconst contentType = response.headers.get('content-type') || '';\n\t\t\tif (!contentType.includes('application/json')) {\n\t\t\t\tconst text = await response.text();\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: 'API_ERROR',\n\t\t\t\t\t\tmessage: `API returned non-JSON response (${response.status}): ${text.substring(0, 100)}...`\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst jsonData = await response.json();\n\t\t\tconst result = jsonData as GenerateBriefResponse;\n\n\t\t\tif (!response.ok || !result.success) {\n\t\t\t\t// Try to extract error from various possible response formats\n\t\t\t\tconst errorMessage =\n\t\t\t\t\tresult.error?.message ||\n\t\t\t\t\t(jsonData as any)?.message ||\n\t\t\t\t\t(jsonData as any)?.error ||\n\t\t\t\t\t`API request failed: ${response.status} - ${response.statusText}`;\n\n\t\t\t\tconst errorCode =\n\t\t\t\t\tresult.error?.code ||","sourceCodeStart":1090,"sourceCodeEnd":1126,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/packages/tm-core/src/modules/integration/services/export.service.ts#L1090-L1126","documentation":"callGenerateBriefEndpoint checks the Content-Type header of the brief-generation HTTP response; if it is not application/json the library reads the body text and returns an API_ERROR result embedding the HTTP status and the first 100 characters. This typically means the request hit an HTML error page, a proxy login page, or a non-JSON gateway response instead of the Hamster API.","triggerScenarios":"The generate-brief endpoint (or an intermediary) responds with text/html or text/plain - e.g. a 502/504 gateway HTML page, an auth-redirect HTML page, a captive portal, or a proxy error page.","commonSituations":"Corporate proxies/VPNs intercepting requests, wrong base URL pointing at a website instead of the API, server outages returning HTML error pages, or rate-limit/WAF pages (Cloudflare) blocking the request.","solutions":["Inspect the embedded body snippet and HTTP status in the message to identify who returned the non-JSON response.","Verify the API base URL / environment configuration points at the Hamster API, not a website.","Check proxy/VPN settings; try from a network without an intercepting proxy.","Retry later if the status is 5xx (server-side outage); check Hamster status page.","If a WAF/rate limiter blocks you, reduce request frequency or contact support."],"exampleFix":"// before (behind intercepting proxy)\nconst res = await fetch('https://wrong-host/briefs/generate', ...);\n// after\nconst res = await fetch('https://api.hamster.example/.../generate', { headers: { Authorization: `Bearer ${token}` } });","handlingStrategy":"try-catch","validationCode":"const probe = await fetch(apiBase, { headers: { Accept: 'application/json' } });\nconst ct = probe.headers.get('content-type') || '';\nif (!ct.includes('application/json')) throw new Error('Endpoint is not returning JSON - check base URL/proxy');","typeGuard":"function isApiError(r: GenerateBriefResult): r is GenerateBriefResult & { success: false; error: { code: 'API_ERROR' } } {\n  return !r.success && r.error?.code === 'API_ERROR';\n}","tryCatchPattern":"const result = await exportService.generateBriefFromTasks(opts);\nif (!result.success && result.error?.code === 'API_ERROR') {\n  // message contains status + body snippet; log it and retry with backoff or alert\n  logger.error('non-JSON API response', { detail: result.error.message });\n}","preventionTips":["Verify the API base URL points at the JSON API, not a website","Bypass/verify intercepting proxies and VPNs in corporate environments","Set Accept: application/json on upstream requests","Monitor Hamster status for outages returning HTML error pages"],"tags":["http","api","network","proxy"],"backgroundTag":"non-json-api-response","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}