hashicorp/terraform · error

Unexpected HTTP response code

Error message

Unexpected HTTP response code %d

What it means

The lock response status code was not one of 200/401/403/409/423. '%d' is the unexpected status. Common culprits: 404 (lock_address points to a non-existent route), 405 (the configured lock_method is not allowed by the server), 500/502/503 (server/gateway error), or 301/302 (redirect the client did not follow).

Solutions

  1. Confirm lock_address is correct: curl -i -X <lock_method> <lock_address>.
  2. If the server does not support the configured method, set lock_method to a verb it accepts (commonly POST or PUT).
  3. For 404, correct the lock_address path or remove it to disable locking.
  4. For 5xx, address the server-side error and retry.
  5. Ensure reverse proxies follow/forward the lock route and do not redirect to a different status.

Example fix

// before (server does not support LOCK verb -> 405)
lock_method   = "LOCK"
// after
lock_method   = "POST"
Defensive patterns

Strategy: validation

Validate before calling

# Pre-flight: confirm the lock endpoint returns an expected status for the method
code=$(curl -sS -o /dev/null -w '%{http_code}' -u "$TF_HTTP_USERNAME:$TF_HTTP_PASSWORD" \
  -X "${TF_HTTP_LOCK_METHOD:-LOCK}" "${TF_HTTP_LOCK_ADDRESS:-$TF_HTTP_ADDRESS}")
case "$code" in
  200|201|204|401|403|409|423) echo "lock endpoint ok ($code)" ;;
  *) echo "ERROR: unexpected lock status $code — check lock_address/lock_method"; exit 1 ;;
esac

Prevention

When it happens

Trigger: lock_address routes to a non-existent path (404); server does not implement the LOCK verb and returns 405 (typical when lock_method=LOCK against a generic web server); server error (500); reverse proxy misroutes the request.

Common situations: lock_address set equal to address but the server has no dedicated lock endpoint; lock_method=LOCK but the server only accepts POST/PUT; reverse proxy rewrites the path incorrectly; server's lock route moved.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/e36a70cd0709a4a2. Report an issue: GitHub.

Appendix: source

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

		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)
	}
}

func (c *httpClient) Unlock(id string) error {
	if c.UnlockURL == nil {
		return nil
	}

	resp, err := c.httpRequest(c.UnlockMethod, c.UnlockURL, &c.jsonLockInfo, "unlock")
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	switch resp.StatusCode {
	case http.StatusOK:
		return nil
	default:

View on GitHub (pinned to d32a084675)