hashicorp/terraform · error

HTTP remote state endpoint requires auth

Error message

HTTP remote state endpoint requires auth

What it means

The lock request returned HTTP 401 Unauthorized. The server requires authentication but none was sent, or the credentials were not recognized as auth at all. Credentials come from username/password attributes or TF_HTTP_USERNAME/TF_HTTP_PASSWORD and are sent as HTTP Basic auth (req.SetBasicAuth).

Solutions

  1. Set username and password (or export TF_HTTP_USERNAME and TF_HTTP_PASSWORD) to valid server credentials.
  2. Confirm the credentials work with: curl -u "$TF_HTTP_USERNAME:$TF_HTTP_PASSWORD" -i <lock_address>.
  3. Re-export the env vars in the same shell/CI step that runs terraform.

Example fix

// before (no creds)
address = "https://state.example.com/terraform"
// after
address  = "https://state.example.com/terraform"
username = var.state_user
password = var.state_pass
Defensive patterns

Strategy: validation

Validate before calling

# Pre-flight: confirm basic auth is accepted by the endpoint
curl -fsS -u "$TF_HTTP_USERNAME:$TF_HTTP_PASSWORD" -o /dev/null -w 'http=%{http_code}\n' "$TF_HTTP_ADDRESS" \
  || { echo 'ERROR: endpoint requires auth or rejected creds'; exit 1; }

Prevention

When it happens

Trigger: Lock request to a server requiring basic auth while username/password are empty, or the server's auth config rejects the provided pair with a 401 (rather than 403).

Common situations: Forgot to set username/password; TF_HTTP_USERNAME/TF_HTTP_PASSWORD not exported in the current shell/CI; server recently had auth enabled; credentials rotated but Terraform config not updated.

Related errors


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

Appendix: source

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

	if c.LockURL == nil {
		return "", nil
	}
	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{

View on GitHub (pinned to d32a084675)