koala73/worldmonitor · error
IMD_AUTH_HTTP_${response.status}
Error message
IMD_AUTH_HTTP_${response.status} What it means
Thrown by mintImdApiToken in scripts/lib/imd-cyclone-marine.mjs when the POST to the IMD OAuth token endpoint returns a non-2xx HTTP status. The library converts the status code into a stable error string (IMD_AUTH_HTTP_<status>) which imdAuthFailureReason later maps to the failure reason surfaced to callers as { token: null, error }. It signals the upstream authentication API rejected the credential request before any token could be minted.
Solutions
- Verify the IMD email/password credentials are current and correctly loaded from env, then retry with valid credentials.
- Log response.status (it is embedded in the thrown message) and if 429 back off and retry later with jitter.
- Check whether IMD's token URL changed; a 301/302/404 means update IMD_OAUTH_TOKEN_URL.
- If 5xx, wait and retry — the failure reason propagates as IMD_AUTH_HTTP_5xx and the worker should retry on the next cycle.
- Confirm the outbound network/proxy can reach the IMD host (a proxy failure surfaces as a different IMD_PROXY_CONNECT_HTTP_* reason).
Example fix
// before
const response = await fetchFn(IMD_OAUTH_TOKEN_URL, { method: 'POST', body: JSON.stringify({ email: process.env.IMD_EMAIL, password: process.env.IMD_PASSWORD }) });
// after
// validate credentials are present before the request; handle specific statuses
if (!process.env.IMD_EMAIL || !process.env.IMD_PASSWORD) throw new Error('IMD credentials missing from env');
const response = await fetchFn(IMD_OAUTH_TOKEN_URL, { method: 'POST', body: JSON.stringify({ email: process.env.IMD_EMAIL, password: process.env.IMD_PASSWORD }) });
if (response.status === 429) await sleep(backoffMs); // then retry Defensive patterns
Strategy: try-catch
Validate before calling
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) || !password) throw new Error('IMD credentials missing or malformed before auth request'); Type guard
function hasImdCredentials(env) { return typeof env.IMD_EMAIL === 'string' && env.IMD_EMAIL.includes('@') && typeof env.IMD_PASSWORD === 'string' && env.IMD_PASSWORD.length > 0; } Try / catch
const { token, error } = await mintImdApiToken({ email, password });
if (!token) {
if (/^IMD_AUTH_HTTP_429$/.test(error)) await sleep(backoffWithJitter());
else if (/^IMD_AUTH_HTTP_5/.test(error)) scheduleRetryNextCycle();
else throw new Error(`IMD auth rejected: ${error}`);
} Prevention
- Keep IMD credentials in env and rotate them before expiry; never hardcode them.
- Add an alert on repeated IMD_AUTH_HTTP_401/403 so credential rot is caught quickly.
- Include a descriptive User-Agent and reputable egress IP to avoid WAF 403s.
- Handle 429 with exponential backoff instead of tight retry loops.
When it happens
Trigger: POST { email, password } to IMD_OAUTH_TOKEN_URL responds with a non-ok status (e.g. 401 for wrong email/password, 403 for blocked IP, 429 for rate limiting, 5xx for IMD outage). redirect: 'error' also makes any redirect surface here as IMD_AUTH_HTTP_3xx.
Common situations: Expired or rotated IMD portal credentials in env config; IMD temporarily blocking datacenter IPs or rate-limiting token mints; IMD moving the OAuth endpoint so requests hit a redirect or 404; IMD-side outage returning 5xx.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- GDACS ${res.status}
- ${operation} HTTP ${status}: ${safeCode}
- Exa ${endpoint.slice(1)} failed HTTP ${response.status}: ${d
- Sign in to view your brief.
- HTTP ${res.status}
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/b9480ea15ad92ada.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/imd-cyclone-marine.mjs:833
password,
fetchFn = globalThis.fetch,
userAgent = CHROME_UA,
maxBytes = IMD_MAX_BYTES,
timeoutMs = IMD_TIMEOUT_MS,
}) {
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');
}View on GitHub (pinned to 7d06c8633d)