hashicorp/terraform · error · ErrUnauthorized

invalid credentials for

Error message

invalid credentials for %s

What it means

`errUnauthorized` tries to derive a `svchost.Hostname` from the final (post-redirect) URL for the `ErrUnauthorized` payload. If that conversion fails — an unusual but tolerated case — it falls back to this plain error message naming the final URL. It is a defensive fallback so the auth-failure path still returns a useful error.

Solutions

  1. Review the redirect chain (`curl -IL`) that leads to the 401/403 and ensure the final host is a normal DNS hostname.
  2. Configure correct credentials for the mirror hostname.
  3. Avoid mirrors that redirect to IP-literal or otherwise unusual hosts.

Example fix

// before: redirect to IP literal triggers fallback
https://mirror.local -> 302 -> https://10.0.0.1/ -> 401
// after: mirror serves directly under a DNS hostname
https://mirror.local/hashicorp/aws/index.json -> 401 // still unauthorized,
// but ErrUnauthorized now carries a real hostname for diagnostics
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate the final URL host is a service hostname
if _, err := svchost.FromString(finalURL.Hostname()); err != nil {
    log.Printf("[WARN] final URL host %q not a service hostname; using raw URL in error", finalURL)
}

Type guard

// finalURLHasValidHost narrows post-redirect URLs
func finalURLHasValidHost(u *url.URL) bool {
    _, err := svchost.FromString(u.Hostname())
    return err == nil
}

Try / catch

hostname, err := svchostFromURL(finalURL)
if err != nil {
    // still return an unauthorized error, just without a structured hostname
    return ErrUnauthorized{Hostname: "", HaveCredentials: true}
}

Prevention

When it happens

Trigger: A 401/403 response is being formatted; `svchostFromURL(finalURL)` returns an error; fallback at http_mirror_source.go:415.

Common situations: Mirror redirected to a URL with a host `svchost` cannot parse (e.g. an IP literal, an empty host, or an unusual scheme); exotic redirect through a non-DNS hostname.

Understand the failure class

Related errors


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

Appendix: source

Thrown at internal/getproviders/http_mirror_source.go:415

func (s *HTTPMirrorSource) errQueryFailed(provider addrs.Provider, err error) error {
	if err == context.Canceled {
		// This one has a special error type so that callers can
		// handle it in a different way.
		return ErrRequestCanceled{}
	}
	return ErrQueryFailed{
		Provider:  provider,
		Wrapped:   err,
		MirrorURL: s.baseURL,
	}
}

func (s *HTTPMirrorSource) errUnauthorized(finalURL *url.URL) error {
	hostname, err := svchostFromURL(finalURL)
	if err != nil {
		// Again, weird but we'll tolerate it.
		return fmt.Errorf("invalid credentials for %s", finalURL)
	}

	return ErrUnauthorized{
		Hostname: hostname,

		// We can't easily tell from here whether we had credentials or
		// not, so for now we'll just assume we did because "host rejected
		// the given credentials" is, hopefully, still understandable in
		// the event that there were none. (If this ends up being confusing
		// in practice then we'll need to do some refactoring of how
		// we handle credentials in this source.)
		HaveCredentials: true,
	}
}

func svchostFromURL(u *url.URL) (svchost.Hostname, error) {
	raw := u.Host

View on GitHub (pinned to d32a084675)