hashicorp/terraform · error

HTTP remote state endpoint invalid auth

Error message

HTTP remote state endpoint invalid auth

What it means

During Lock(), the server returned 403 Forbidden: credentials were accepted but are not permitted to lock this state. Distinct from 401 (error 250) which is missing/unrecognized credentials.

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

Solutions

  1. Grant the configured principal permission to lock (and unlock) the state path on the server.
  2. Verify with `curl -u user:pass -X LOCK <lock_address>` returns 200, not 403.
  3. Switch to a credential that has the required lock/unlock ACLs.

Example fix

// before: token scoped read-only
// after: grant lock+unlock on the state path
// policy on server (example):
//   path "state/*" { capabilities = ["create","read","update","delete","lock","unlock"] }
Defensive patterns

Strategy: validation

Validate before calling

// Verify lock permission against the real endpoint before terraform apply
func canLock(lockURL, user, pass string) error {
  req, _ := http.NewRequest("LOCK", lockURL, nil)
  req.SetBasicAuth(user, pass)
  resp, err := http.DefaultClient.Do(req)
  if err != nil { return err }
  defer resp.Body.Close()
  if resp.StatusCode == http.StatusForbidden {
    return fmt.Errorf("principal lacks lock permission")
  }
  return nil
}

Prevention

When it happens

Trigger: Valid user lacking the lock ACL on the workspace; correct token scoped read-only; path-level policy denying the LOCK method on the state endpoint. Fires at lock acquisition time during apply/plan/destroy.

Common situations: Service account rotated to lower privileges; new workspace with default restrictive policy; token environment variable pointing at a CI deploy token instead of a state-management token.

Related errors


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