wtfutil/wtf · error
unexpected status %d from DEV.to API
Error message
unexpected status %d from DEV.to API
What it means
FetchArticles in modules/devto/client.go only accepts HTTP 200 from the DEV.to /articles endpoint; any other status (401, 403, 422, 429, 5xx) is rejected with this message. The library does not inspect the body on failure, so the status code is the only diagnostic returned. It wraps nothing — the raw status number is interpolated into the message.
Source
Thrown at modules/devto/client.go:74
}
if perPage > 0 {
q.Set("per_page", fmt.Sprintf("%d", perPage))
}
u.RawQuery = q.Encode()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {
return nil, fmt.Errorf("creating request: %w", err)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("executing request: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status %d from DEV.to API", resp.StatusCode)
}
var articles []Article
if err := json.NewDecoder(resp.Body).Decode(&articles); err != nil {
return nil, fmt.Errorf("decoding response: %w", err)
}
return articles, nil
}
View on GitHub (pinned to bb838c1ccb)
Solutions
- Verify the API key passed to the client is set and valid (curl -H 'api-key: ...' https://dev.to/api/articles/me).
- Log the full status code; if 429, back off and increase the widget refresh interval.
- If 422, check the query parameters built into the request URL for invalid values.
- If 5xx, retry later — the failure is server-side, not a client bug.
Example fix
// before
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status %d from DEV.to API", resp.StatusCode)
}
// after
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 512))
return nil, fmt.Errorf("unexpected status %d from DEV.to API: %s", resp.StatusCode, body)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go: validate config before calling
if apiKey == "" {
return fmt.Errorf("DEV.to API key is not configured")
} Type guard
func isStatusError(err error) (int, bool) {
msg := err.Error()
var code int
if _, e := fmt.Sscanf(msg, "unexpected status %d from DEV.to API", &code); e == nil {
return code, true
}
return 0, false
} Try / catch
articles, err := client.FetchArticles(ctx)
if err != nil {
var code int
if fmt.Sscanf(err.Error(), "unexpected status %d from DEV.to API", &code) == nil && code == http.StatusTooManyRequests {
time.Sleep(backoff) // retry later
return
}
log.Printf("devto fetch failed: %v", err)
return
} Prevention
- Keep the DEV.to API key in env/config and verify it with a curl call before wiring it in.
- Set a refresh interval well above DEV.to rate limits.
- Log status codes so 429 vs 401 vs 5xx are distinguishable in production.
When it happens
Trigger: Any non-200 response from the DEV.to API during FetchArticles: missing/invalid api-key header (401/403), malformed query params like page or tag (422), rate limiting (429), or DEV.to server errors (500/502/503).
Common situations: Expired or unset DEV_TO_API_KEY, hitting DEV.to's rate limit during frequent refreshes, transient DEV.to outages, or proxy/gateway returning an HTML error page with a non-200 status.
Related errors
AI-assisted analysis of wtfutil/wtf@bb838c1ccb (2026-09-03).
Data as JSON: /api/errors/154b2bea835a2ed6.
Report an issue: GitHub.