{"record":{"id":"d29c5a5fea23e4eb","repo":"hashicorp/terraform","slug":"http-error-d","errorCode":null,"errorMessage":"HTTP error: %d","messagePattern":"HTTP error: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/http/client.go","lineNumber":241,"sourceCode":"\t\t}\n\t*/\n\n\tvar method string = \"POST\"\n\tif c.UpdateMethod != \"\" {\n\t\tmethod = c.UpdateMethod\n\t}\n\tresp, err := c.httpRequest(method, &base, &data, \"upload state\")\n\tif err != nil {\n\t\treturn diags.Append(err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// Handle the error codes\n\tswitch resp.StatusCode {\n\tcase http.StatusOK, http.StatusCreated, http.StatusNoContent:\n\t\treturn diags\n\tdefault:\n\t\treturn diags.Append(fmt.Errorf(\"HTTP error: %d\", resp.StatusCode))\n\t}\n}\n\nfunc (c *httpClient) Delete() tfdiags.Diagnostics {\n\tvar diags tfdiags.Diagnostics\n\n\t// Make the request\n\tresp, err := c.httpRequest(\"DELETE\", c.URL, nil, \"delete state\")\n\tif err != nil {\n\t\treturn diags.Append(err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// Handle the error codes\n\tswitch resp.StatusCode {\n\tcase http.StatusOK:\n\t\treturn diags\n\tdefault:","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/http/client.go#L223-L259","documentation":"Thrown by Put() (upload state) when the POST/PUT response status is not 200/201/204 (client.go:236-242). The %d is the status code. This fires during terraform apply/destroy when writing the new state back to the HTTP endpoint.","triggerScenarios":"Upload returns 409 (lock held / conflict), 413 (payload too large), 401/403 (auth), 500 (server error), 405 (method not allowed), or any other non-2xx. Common during state push after a plan when the server rejects the write.","commonSituations":"State lock held by another run (409); reverse proxy body-size limit exceeded by a large state (413); wrong UpdateMethod configured (e.g., PUT against an endpoint expecting POST → 405); auth token expired between GET and PUT.","solutions":["Map the numeric code: 409 → force-unlock or wait for the other run; 413 → raise proxy/server body limit; 401/403 → fix auth; 405 → set the correct update_method.","Verify update_method (default POST) matches what the endpoint accepts.","For large states, raise nginx/proxy client_max_body_size or equivalent.","Confirm the lock ID query parameter is being sent if the endpoint enforces locking."],"exampleFix":"// before\nbackend \"http\" {\n  address = \"https://state.example.com/tf\"\n  # default update_method = \"POST\" but endpoint expects PUT\n}\n// after\nbackend \"http\" {\n  address       = \"https://state.example.com/tf\"\n  update_method = \"PUT\"\n}","handlingStrategy":"try-catch","validationCode":"// Pre-check that the endpoint accepts the configured method and body size:\n// req, _ := http.NewRequest(updateMethod, url, bytes.NewReader(sample))\n// req.SetBasicAuth(user, pass); req.Header.Set(\"Content-Type\",\"application/json\")\n// resp, _ := client.Do(req) // expect 200/201/204","typeGuard":null,"tryCatchPattern":"// diags := httpClient.Put(data)\n// for _, d := range diags {\n//   if strings.Contains(d.Description().Summary, \"HTTP error\") {\n//     code := parseCode(d.Description().Summary)\n//     switch code { case 409: forceUnlockOrWait(); case 413: raiseBodyLimit() }\n//   }\n// }","preventionTips":["Match update_method to what the endpoint supports (POST vs PUT).","Set proxy/server body-size limits above your largest expected state.","Acquire and pass the lock ID on writes when the endpoint enforces locking."],"tags":["http-backend","upload","remote-state","write"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}