{"record":{"id":"36c78b2d9547c3cd","repo":"hashicorp/terraform","slug":"http-remote-state-already-locked-failed-to-read-b","errorCode":null,"errorMessage":"HTTP remote state already locked, failed to read body","messagePattern":"HTTP remote state already locked, failed to read body","errorType":"http","errorClass":"LockError","httpStatus":409,"severity":"error","filePath":"internal/backend/remote-state/http/client.go","lineNumber":107,"sourceCode":"\t\treturn \"\", err\n\t}\n\tdefer resp.Body.Close()\n\n\tswitch resp.StatusCode {\n\tcase http.StatusOK:\n\t\tc.lockID = info.ID\n\t\tc.jsonLockInfo = jsonLockInfo\n\t\treturn info.ID, nil\n\tcase http.StatusUnauthorized:\n\t\treturn \"\", fmt.Errorf(\"HTTP remote state endpoint requires auth\")\n\tcase http.StatusForbidden:\n\t\treturn \"\", fmt.Errorf(\"HTTP remote state endpoint invalid auth\")\n\tcase http.StatusConflict, http.StatusLocked:\n\t\tdefer resp.Body.Close()\n\t\tbody, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn \"\", &statemgr.LockError{\n\t\t\t\tErr: fmt.Errorf(\"HTTP remote state already locked, failed to read body\"),\n\t\t\t}\n\t\t}\n\t\texisting := statemgr.LockInfo{}\n\t\terr = json.Unmarshal(body, &existing)\n\t\tif err != nil {\n\t\t\treturn \"\", &statemgr.LockError{\n\t\t\t\tErr: fmt.Errorf(\"HTTP remote state already locked, failed to unmarshal body\"),\n\t\t\t}\n\t\t}\n\t\treturn \"\", &statemgr.LockError{\n\t\t\tInfo: &existing,\n\t\t\tErr:  fmt.Errorf(\"HTTP remote state already locked: ID=%s\", existing.ID),\n\t\t}\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"Unexpected HTTP response code %d\", resp.StatusCode)\n\t}\n}\n","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/http/client.go#L89-L125","documentation":"The lock endpoint returned 409 Conflict or 423 Locked (state is already locked), but io.ReadAll(resp.Body) then failed while reading the response body. The network connection dropped mid-read (reset, TLS close, proxy truncation). The error is wrapped in a statemgr.LockError so Terraform treats it as a lock failure.","triggerScenarios":"409/423 received and headers parsed, then the body read errored — connection reset by peer, TLS handshake layer closed mid-stream, idle timeout killed the connection, or an intermediate proxy truncated the response.","commonSituations":"Flaky network or aggressive load balancer idle timeout; server closes the connection right after sending headers; proxy buffering issue; high-latency link dropping packets.","solutions":["Retry terraform apply — transient body-read failures usually clear on the next attempt.","Increase server/proxy idle timeouts to exceed terraform's request duration.","Check for connection resets in server/proxy logs and stabilize the network path.","If recurring, raise retry_max so retryablehttp re-attempts the lock request."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"# Pre-flight: check for connection resets / idle timeouts against the lock endpoint\ncurl -sS -u \"$TF_HTTP_USERNAME:$TF_HTTP_PASSWORD\" -X \"${TF_HTTP_LOCK_METHOD:-LOCK}\" \\\n  -i --max-time 10 \"${TF_HTTP_LOCK_ADDRESS:-$TF_HTTP_ADDRESS}\" | head -1","typeGuard":null,"tryCatchPattern":"# Transient body-read failure: re-run terraform; retryablehttp will re-attempt the lock.\nfor i in 1 2 3; do\n  terraform apply -auto-approve && break\n  echo \"retry ($i/3)...\"; sleep 5\ndone","preventionTips":["Raise server/proxy idle timeouts above terraform's longest request.","Keep retry_max > 0 so transport retries kick in.","Monitor for connection resets in the network path between runner and state server."],"tags":["network","lock","http-backend","transient","lock-error"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}