koala73/worldmonitor · error
IMD_AUTH_RESPONSE_INVALID
Error message
IMD_AUTH_RESPONSE_INVALID
What it means
Thrown by mintImdApiToken when the auth endpoint returned HTTP 2xx but readBoundedJsonResponse could not parse a bounded JSON body. The only parse-stage error that passes through unchanged is IMD_RESPONSE_TOO_LARGE:<bytes>; everything else (invalid JSON, empty body, truncated response, non-JSON content type) is normalized to IMD_AUTH_RESPONSE_INVALID. imdAuthFailureReason maps it 1:1 to the caller-facing failure reason.
Solutions
- Log the raw response body (within bounds) for one failing request to see what the endpoint actually returned.
- If it's HTML/WAF challenge content, the request needs different headers or must come from an allowlisted IP.
- If the API contract changed, update the payload parsing to match the new format.
- If the body is intermittently truncated, increase timeoutMs or retry; persistent truncation suggests a proxy issue.
- Ensure maxBytes is left at the default (IMD_MAX_BYTES) unless a token response legitimately exceeds it — then handle the RESPONSE_TOO_LARGE path instead.
Example fix
// before
payload = await readBoundedJsonResponse(response, maxBytes);
// after
const text = await response.text();
try { payload = JSON.parse(text); }
catch { console.error('IMD auth body head:', text.slice(0, 200)); throw new Error('IMD_AUTH_RESPONSE_INVALID'); } Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
function isOAuthTokenPayload(p) { return p !== null && typeof p === 'object' && typeof p.access_token === 'string' && p.access_token.length > 0 && typeof p.token_type === 'string' && typeof p.expires_in === 'number' && Number.isFinite(p.expires_in) && p.expires_in > 0; } Try / catch
const { token, error } = await mintImdApiToken({ email, password });
if (error === 'IMD_AUTH_RESPONSE_INVALID') {
// inspect body once, then retry with backoff in case of truncated/CDN response
await sleep(RETRY_MS);
return mintImdApiToken({ email, password });
} Prevention
- Log a bounded head of unexpected bodies (first ~200 chars) for diagnosis, redacting tokens.
- Watch for IMD/WAF contract changes after their deployments; alert on the first INVALID occurrence.
- Keep maxBytes at the library default so size handling follows the intended RESPONSE_TOO_LARGE path.
- Prefer retrying transiently over failing the whole cycle for a single bad response.
When it happens
Trigger: IMD_OAUTH_TOKEN_URL returns 200 with a body that is not valid JSON, an empty body, HTML (e.g. a login page or WAF challenge), or a body larger than maxBytes that fails the bounded read with an error other than the RESPONSE_TOO_LARGE marker.
Common situations: IMD behind a CDN/WAF serving an HTML challenge page with status 200; IMD API contract change (new response format); captive proxy injecting content; truncated gzip/plain body from a flaky connection.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- Exa returned malformed structured summary for ${url}
- Sign in to view your brief.
- HTTP ${res.status}
- Redis EVAL returned an invalid response
- Redis returned malformed JSON
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/ce88a37e6e38b150.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/imd-cyclone-marine.mjs:839
try {
const response = await fetchFn(IMD_OAUTH_TOKEN_URL, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'User-Agent': userAgent,
},
body: JSON.stringify({ email, password }),
redirect: 'error',
signal: AbortSignal.timeout(timeoutMs),
});
if (!response.ok) throw new Error(`IMD_AUTH_HTTP_${response.status}`);
let payload;
try {
payload = await readBoundedJsonResponse(response, maxBytes);
} catch (err) {
if (/^IMD_RESPONSE_TOO_LARGE:\d+$/.test(String(err?.message || ''))) throw err;
throw new Error('IMD_AUTH_RESPONSE_INVALID');
}
const accessToken = typeof payload?.access_token === 'string' ? payload.access_token : '';
const tokenType = typeof payload?.token_type === 'string' ? payload.token_type.trim() : '';
const expiresIn = Number(payload?.expires_in);
if (
!/^[\u0021-\u007E]+$/.test(accessToken)
|| tokenType.toLowerCase() !== 'bearer'
|| !Number.isFinite(expiresIn)
|| expiresIn <= 0
) {
throw new Error('IMD_AUTH_RESPONSE_INVALID');
}
return { token: accessToken, error: null };
} catch (err) {
return { token: null, error: imdAuthFailureReason(err) };
}
}
View on GitHub (pinned to 7d06c8633d)