hashicorp/terraform · error

HTTP remote state endpoint invalid auth

Error message

HTTP remote state endpoint invalid auth

What it means

The lock request returned HTTP 403 Forbidden. Credentials were accepted as authentication but the authenticated principal is not permitted to perform the lock operation on this endpoint. Distinct from 401 (no/bad auth) — here the server knows who you are but denies the action.

Solutions

  1. Grant the principal lock/write permission on the state endpoint (server-side RBAC/ACL).
  2. Switch to a service account with the required role.
  3. Confirm with curl -u user:pass -X <lock_method> -i <lock_address> that a 2xx is returned once permissions are fixed.

Example fix

// Role/policy change required server-side; e.g. grant LOCK on /terraform/*
// then verify:
//   curl -u "$U:$P" -X LOCK -i https://state.example.com/lock
Defensive patterns

Strategy: validation

Validate before calling

# Pre-flight: confirm the principal can perform a lock-shaped request
curl -sS -u "$TF_HTTP_USERNAME:$TF_HTTP_PASSWORD" -X "${TF_HTTP_LOCK_METHOD:-LOCK}" \
  -o /dev/null -w 'http=%{http_code}\n' "${TF_HTTP_LOCK_ADDRESS:-$TF_HTTP_ADDRESS}" \
  | grep -qE 'http=(200|201|204|409|423)' || echo 'WARN: principal may lack lock permission'

Prevention

When it happens

Trigger: Valid credentials but the user/service account lacks write/lock permission on the state endpoint; RBAC or ACL policy denies the LOCK method or the resource.

Common situations: Read-only service account used for a backend that writes; server-side permission changed; correct user but wrong role; endpoint requires a specific group/claim the principal lacks.

Related errors


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

Appendix: source

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

	}
	c.lockID = ""

	jsonLockInfo := info.Marshal()
	resp, err := c.httpRequest(c.LockMethod, c.LockURL, &jsonLockInfo, "lock")
	if err != nil {
		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),

View on GitHub (pinned to d32a084675)