hashicorp/terraform · error · statemgr.LockError

HTTP remote state already locked, failed to read body

Error message

HTTP remote state already locked, failed to read body

What it means

The server returned 409 Conflict or 423 Locked (state is held by another run), so the backend tries to read the JSON lock-info body to report who owns the lock — but io.ReadAll(resp.Body) failed, typically because the connection was closed mid-body or a read timeout fired. The result is a statemgr.LockError wrapping this generic message, so the original holder's identity is unknown.

Source

Thrown at internal/backend/remote-state/http/client.go:107

		return "", err
	}
	defer resp.Body.Close()

	switch resp.StatusCode {
	case http.StatusOK:
		c.lockID = info.ID
		c.jsonLockInfo = jsonLockInfo
		return info.ID, nil
	case http.StatusUnauthorized:
		return "", fmt.Errorf("HTTP remote state endpoint requires auth")
	case http.StatusForbidden:
		return "", fmt.Errorf("HTTP remote state endpoint invalid auth")
	case http.StatusConflict, http.StatusLocked:
		defer resp.Body.Close()
		body, err := io.ReadAll(resp.Body)
		if err != nil {
			return "", &statemgr.LockError{
				Err: fmt.Errorf("HTTP remote state already locked, failed to read body"),
			}
		}
		existing := statemgr.LockInfo{}
		err = json.Unmarshal(body, &existing)
		if err != nil {
			return "", &statemgr.LockError{
				Err: fmt.Errorf("HTTP remote state already locked, failed to unmarshal body"),
			}
		}
		return "", &statemgr.LockError{
			Info: &existing,
			Err:  fmt.Errorf("HTTP remote state already locked: ID=%s", existing.ID),
		}
	default:
		return "", fmt.Errorf("Unexpected HTTP response code %d", resp.StatusCode)
	}
}

View on GitHub (pinned to c9def3e214)

Solutions

  1. Retry the terraform run; transient body-read failures often succeed on the next attempt.
  2. Inspect the state server logs to confirm it is sending a complete JSON body for 409/423 responses.
  3. Increase proxy/load-balancer client-body timeouts for the state endpoints.
  4. If another run legitimately holds the lock, run `terraform force-unlock <ID>` once you confirm it is safe (you will not have the ID here, so inspect server-side lock records).
Defensive patterns

Strategy: retry

Type guard

import "errors"

func isLockBodyReadErr(err error) bool {
  var le *statemgr.LockError
  return errors.As(err, &le) && le.Err != nil && strings.Contains(le.Err.Error(), "failed to read body")
}

Try / catch

for attempt := 0; attempt < 3; attempt++ {
  err = sm.Lock(info)
  if err == nil { break }
  if isLockBodyReadErr(err) { backoff(); continue }
  return err
}

Prevention

When it happens

Trigger: Concurrent terraform apply on shared state where the lock-info response body is truncated or the connection drops; an interceptor (proxy/IDS) closing 4xx responses early; a server returning 423 with an empty body that the reader surfaces as an unexpected EOF.

Common situations: Flaky network between CI and state server; aggressive proxy timeout on error responses; custom state server not returning a body on 409/423.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/36c78b2d9547c3cd. Report an issue: GitHub.