Tencent/WeKnora · error
unexpected status %d: %s
Error message
unexpected status %d: %s
What it means
The Notion API returned an HTTP status outside all explicitly handled ranges (not 2xx, 401/403, 404, 429, or 5xx). Unlike retryable cases, this returns immediately with no retry. The message includes the status code and raw response body, which usually contains Notion's JSON error object describing the actual problem.
Source
Thrown at internal/datasource/connector/notion/client.go:144
lastErr = fmt.Errorf("rate limited: %s", string(respBody))
if attempt < maxRetries {
if sErr := sleepWithContext(ctx, wait); sErr != nil {
return nil, sErr
}
continue
}
case resp.StatusCode >= 500:
lastErr = fmt.Errorf("server error %d: %s", resp.StatusCode, string(respBody))
if attempt < maxRetries {
if sErr := sleepWithContext(ctx, time.Duration(1<<attempt)*time.Second); sErr != nil {
return nil, sErr
}
continue
}
default:
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(respBody))
}
}
if lastErr != nil {
return nil, fmt.Errorf("%w: %v", datasource.ErrFetchFailed, lastErr)
}
return nil, datasource.ErrFetchFailed
}
// Ping verifies the API token is valid by calling GET /v1/users/me.
func (c *notionClient) Ping(ctx context.Context) error {
_, err := c.doRequest(ctx, http.MethodGet, "/v1/users/me", nil)
return err
}
// SearchPages returns all pages and databases accessible to the integration.
func (c *notionClient) SearchPages(ctx context.Context) ([]notionPage, error) {
return c.paginatePages(ctx, http.MethodPost, "/v1/search")View on GitHub (pinned to 988cbb0330)
Solutions
- Read the JSON body in the error message — Notion's code/message fields (e.g. validation_error, conflict_error) identify the real cause.
- Validate Notion page/database/block IDs before calling (32-hex or UUID format) — 400 validation_error usually means a bad ID or path.
- Check that NotionAPIVersion matches the endpoints being used; data_sources endpoints need API version 2025-09-03 or newer.
- For 409 conflict errors, simply retry the request — it's transient.
- If a new status keeps appearing, add an explicit case in the switch at client.go:109 to handle it (e.g. map 400 to a typed error).
Example fix
// before
default:
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(respBody))
// after: surface 400/409 explicitly
case resp.StatusCode == http.StatusBadRequest:
return nil, fmt.Errorf("%w: %s", datasource.ErrBadRequest, string(respBody))
case resp.StatusCode == http.StatusConflict:
// retryable: fall through to retry logic
lastErr = fmt.Errorf("conflict: %s", string(respBody)) Defensive patterns
Strategy: validation
Validate before calling
var notionIDRe = regexp.MustCompile(`^[0-9a-fA-F]{32}$|^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
func validNotionID(id string) bool { return notionIDRe.MatchString(strings.ReplaceAll(id, "-", "")) || notionIDRe.MatchString(id) } Try / catch
if err != nil && strings.Contains(err.Error(), "unexpected status 4") {
// parse the JSON body from the error for notion's code field
var apiErr struct{ Code string `json:"code"`; Message string `json:"message"` }
// log apiErr.Code: validation_error vs conflict_error
} Prevention
- Validate page/database/block ID format before calling the API
- Keep NotionAPIVersion pinned and consistent with the endpoints used
- Treat 409 as retryable — re-issue the request once
- Map frequent statuses to typed errors in the switch for better handling
When it happens
Trigger: Any doRequest call receiving a status like 400 (validation_error), 409 (conflict_error / rate_limited variant), 410, or 3xx redirects. E.g. GetPage with a malformed page ID → 400; a 409 conflict from a concurrent edit; a 400 validation error on the search/query POST body.
Common situations: Malformed Notion IDs passed in (missing dashes usually OK, but truncated IDs → 400 validation_error); 409 conflicts when content changes mid-sync; API version mismatch (Notion-Version header) causing rejected requests; an endpoint contract change (e.g. data_sources endpoints require 2025-09-03+).
Related errors
- get block children for %s: %w
- paginate %s: %w
- %w: missing api_key
- %w: api_key must be a non-empty string
- Zhipu API returned status %d (%s): %s
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/13723827ad2313a7.
Report an issue: GitHub.