{"record":{"id":"0396036f90e15303","repo":"cloudflare/cloudflared","slug":"api-errors-s","errorCode":null,"errorMessage":"API errors: %s","messagePattern":"API errors: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cfapi/base_client.go","lineNumber":209,"sourceCode":"type Pagination struct {\n\tCount      int `json:\"count,omitempty\"`\n\tPage       int `json:\"page,omitempty\"`\n\tPerPage    int `json:\"per_page,omitempty\"`\n\tTotalCount int `json:\"total_count,omitempty\"`\n}\n\nfunc (r *response) checkErrors() error {\n\tif len(r.Errors) == 0 {\n\t\treturn nil\n\t}\n\tif len(r.Errors) == 1 {\n\t\treturn r.Errors[0]\n\t}\n\tvar messagesBuilder strings.Builder\n\tfor _, e := range r.Errors {\n\t\tmessagesBuilder.WriteString(fmt.Sprintf(\"%s; \", e))\n\t}\n\treturn fmt.Errorf(\"API errors: %s\", messagesBuilder.String())\n}\n\ntype apiError struct {\n\tCode    json.Number `json:\"code,omitempty\"`\n\tMessage string      `json:\"message,omitempty\"`\n}\n\nfunc (e apiError) Error() string {\n\treturn fmt.Sprintf(\"code: %v, reason: %s\", e.Code, e.Message)\n}\n\nfunc (r *RESTClient) statusCodeToError(op string, resp *http.Response) error {\n\tif resp.Header.Get(\"Content-Type\") == \"application/json\" {\n\t\tvar errorsResp response\n\t\tif json.NewDecoder(resp.Body).Decode(&errorsResp) == nil {\n\t\t\tif err := errorsResp.checkErrors(); err != nil {\n\t\t\t\treturn errors.Errorf(\"Failed to %s: %s\", op, err)\n\t\t\t}","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/cfapi/base_client.go#L191-L227","documentation":"cfapi/base_client.go's (*response).checkErrors converts the errors array of a Cloudflare API response envelope into a Go error. When the envelope contains exactly one error it returns that structured apiError; when there are multiple, it concatenates all their messages with \"; \" separators into a single \"API errors: ...\" error. This surfaces validation/rejection details returned by the Cloudflare API for a failed request.","triggerScenarios":"checkErrors is invoked from parseResponseEnvelope and statusCodeToError whenever the decoded response envelope has len(Errors) > 1 — the Cloudflare API returned HTTP 2xx/4xx/5xx with multiple error entries (e.g. several validation failures for one request).","commonSituations":"Batching operations (DNS record updates, tunnel config changes) where several fields fail validation at once; an invalid API token triggering multiple authorization error entries; rate/plan limits reported alongside a validation error; malformed zone or account IDs producing compound errors.","solutions":["Parse the joined message — each ';'-separated entry is one API error; fix the reported fields/IDs and retry.","Inspect HTTP status and the structured apiError (Code/Message) via statusCodeToError for the primary cause.","Validate request payloads (zone IDs, record names, TTLs) against Cloudflare API docs before sending.","Check API token permissions/scopes if messages indicate authentication/authorization failures.","Apply backoff and retry if errors indicate transient rate limiting (error code 9xx/rate limit headers)."],"exampleFix":"// before: log-and-continue on envelope errors\nresp, _ := client.ZoneDetails(ctx, zoneID)\n// after: surface compound API errors\nresp, err := client.ZoneDetails(ctx, zoneID)\nif err != nil {\n\tvar apiErr *cfapi.apiError\n\tif errors.As(err, &apiErr) {\n\t\tlog.Error().Str(\"code\", apiErr.Code.String()).Msg(apiErr.Message)\n\t}\n\treturn fmt.Errorf(\"cloudflare api call failed: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// validate identifiers before the API call\nif zoneID == \"\" {\n    return errors.New(\"zone ID must not be empty\")\n}\nif !strings.Contains(accountTag, \"-\") && len(accountTag) != 32 {\n    return fmt.Errorf(\"account tag %q is not a valid identifier\", accountTag)\n}","typeGuard":"func isCompoundAPIError(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"API errors: \")\n}","tryCatchPattern":"err := resp.checkErrors()\nif err != nil {\n    if isCompoundAPIError(err) {\n        for _, part := range strings.Split(strings.TrimPrefix(err.Error(), \"API errors: \"), \"; \") {\n            log.Warn().Msg(\"cloudflare api error: \" + part)\n        }\n    }\n    return fmt.Errorf(\"cloudflare request rejected: %w\", err)\n}","preventionTips":["Validate IDs, names, and payload fields against the Cloudflare API schema before sending","Check API token scope/permissions when errors mention authentication","Log the full envelope (Messages + Errors) to see every compound error entry","Back off and retry only on documented retryable codes (rate limits)"],"tags":["api","cloudflare","http","validation"],"backgroundTag":"api-error-response","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}