{"record":{"id":"da53a34f03742a1e","repo":"anomalyco/sst","slug":"failed-to-upload-assets-http-d-s","errorCode":null,"errorMessage":"failed to upload assets: HTTP %d %s","messagePattern":"failed to upload assets: HTTP (.+?) (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/server/resource/cloudflare-worker-assets.go","lineNumber":283,"sourceCode":"\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treq.Header.Set(\"Content-Type\", \"multipart/form-data; boundary=\"+writer.Boundary())\n\treq.Header.Set(\"Authorization\", \"Bearer \"+jwt)\n\n\tclient := &http.Client{Timeout: 30 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer resp.Body.Close()\n\n\t// API returns:\n\t// - 202 Accepted if there are more buckets to upload\n\t// - 201 Created if all buckets have been uploaded\n\tif resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusAccepted {\n\t\tresponseBody, _ := io.ReadAll(resp.Body)\n\t\treturn \"\", fmt.Errorf(\"failed to upload assets: HTTP %d %s\", resp.StatusCode, string(responseBody))\n\t}\n\n\t// Decode response\n\tvar result struct {\n\t\tResult UploadResponse `json:\"result\"`\n\t}\n\tif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn result.Result.Jwt, nil\n}","sourceCodeStart":265,"sourceCodeEnd":295,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/pkg/server/resource/cloudflare-worker-assets.go#L265-L295","documentation":"Cloudflare's Workers Assets upload API responds 201 Created when all buckets are uploaded and 202 Accepted when more buckets remain. Any other status is treated as a failed upload and this error includes the HTTP status code plus the response body for diagnosis. It indicates Cloudflare rejected the request — usually auth, account, or payload problems.","triggerScenarios":"POST to /client/v4/accounts/{accountId}/workers/assets/upload?base64=true returns a status other than 201 or 202 (pkg/server/resource/cloudflare-worker-assets.go:283). Typical statuses: 401/403 (bad or missing API token, token lacking Workers Scripts Edit), 404 (wrong accountId or assets upload session not initialized), 400/413 (malformed multipart body, hash mismatch, or payload too large per-bucket limits).","commonSituations":"Expired or revoked Cloudflare API token; token missing the Workers Assets permission; CLOUDFLARE_ACCOUNT_ID mismatch with the token's account; uploading more assets than fit in one bucket without following the 202 continuation; changed upload-session metadata causing hash mismatch.","solutions":["Read the responseBody in the error — Cloudflare includes an errors[] array with codes/messages","Verify the API token is valid and has Workers Scripts/Assets edit permission for the account","Confirm accountId matches the account the worker/assets upload session was created in","Check asset hashes match what the completion session expects (recompute and re-upload changed files)","For 202 responses ensure the caller continues uploading remaining buckets until 201","Retry after a delay on 429/5xx, respecting Cloudflare rate limits"],"exampleFix":"// before\nif resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusAccepted {\n\tresponseBody, _ := io.ReadAll(resp.Body)\n\treturn \"\", fmt.Errorf(\"failed to upload assets: HTTP %d %s\", resp.StatusCode, string(responseBody))\n}\n// after\nif resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusAccepted {\n\tresponseBody, _ := io.ReadAll(resp.Body)\n\tif resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {\n\t\treturn \"\", fmt.Errorf(\"cloudflare auth rejected asset upload (HTTP %d): check API token and permissions: %s\", resp.StatusCode, string(responseBody))\n\t}\n\treturn \"\", fmt.Errorf(\"failed to upload assets: HTTP %d %s\", resp.StatusCode, string(responseBody))\n}","handlingStrategy":"retry","validationCode":"// before upload: verify token and account\nreq, _ := http.NewRequest(\"GET\", \"https://api.cloudflare.com/client/v4/accounts/\"+accountId, nil)\nreq.Header.Set(\"Authorization\", \"Bearer \"+apiToken)\nresp, err := http.DefaultClient.Do(req)\nif err != nil || resp.StatusCode != 200 {\n\treturn fmt.Errorf(\"cloudflare token/account invalid before asset upload (HTTP %v)\", resp.StatusCode)\n}","typeGuard":"func isUploadAccepted(status int) bool {\n\treturn status == http.StatusCreated || status == http.StatusAccepted\n}","tryCatchPattern":"resp, err := http.DefaultClient.Do(req)\nif err != nil {\n\treturn \"\", fmt.Errorf(\"asset upload request failed: %w\", err)\n}\nif !isUploadAccepted(resp.StatusCode) {\n\tresponseBody, _ := io.ReadAll(resp.Body)\n\tif resp.StatusCode == 429 || resp.StatusCode >= 500 {\n\t\t// retry with backoff\n\t}\n\treturn \"\", fmt.Errorf(\"failed to upload assets: HTTP %d %s\", resp.StatusCode, string(responseBody))\n}","preventionTips":["Keep the Cloudflare API token valid with Workers edit permissions","Confirm the accountId matches the token's account","Handle 202 by continuing to upload remaining buckets until 201","Retry 429/5xx with exponential backoff","Always log the response body — Cloudflare error codes pinpoint the cause"],"tags":["cloudflare","http","workers-assets","upload","api"],"backgroundTag":"cloudflare-api-upload-rejected","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}