juanfont/headscale · error · errOIDCClientSecretRequired
oidc.client_secret or oidc.client_secret_path is required wh
Error message
oidc.client_secret or oidc.client_secret_path is required when oidc.issuer is set
What it means
errOIDCClientSecretRequired is an unexported config validation error in hscontrol/types/config.go:38 returned at config.go:392 when oidc.issuer is set but neither oidc.client_secret nor oidc.client_secret_path is provided. Exactly one secret source is mandatory; setting both trips errOidcMutuallyExclusive instead.
Source
Thrown at hscontrol/types/config.go:38
"go4.org/netipx"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
"tailscale.com/util/set"
)
const (
PKCEMethodPlain string = "plain"
PKCEMethodS256 string = "S256"
defaultNodeStoreBatchSize = 100
)
var (
errOidcMutuallyExclusive = errors.New("oidc_client_secret and oidc_client_secret_path are mutually exclusive")
errOIDCIssuerInvalid = errors.New("oidc.issuer must be a valid http(s) URL")
errOIDCClientIDRequired = errors.New("oidc.client_id is required when oidc.issuer is set")
errOIDCClientSecretRequired = errors.New("oidc.client_secret or oidc.client_secret_path is required when oidc.issuer is set")
errServerURLSuffix = errors.New("server_url cannot be part of base_domain in a way that could make the DERP and headscale server unreachable")
errServerURLSame = errors.New("server_url cannot use the same domain as base_domain in a way that could make the DERP and headscale server unreachable")
errInvalidPKCEMethod = errors.New("pkce.method must be either 'plain' or 'S256'")
errTrustedProxyZeroRange = errors.New("0.0.0.0/0 and ::/0 are not allowed")
ErrNoPrefixConfigured = errors.New("no IPv4 or IPv6 prefix configured, minimum one prefix is required")
ErrInvalidAllocationStrategy = errors.New("invalid prefix allocation strategy")
)
type IPAllocationStrategy string
const (
IPAllocationStrategySequential IPAllocationStrategy = "sequential"
IPAllocationStrategyRandom IPAllocationStrategy = "random"
)
type PolicyMode string
const (View on GitHub (pinned to 565fd254d0)
Solutions
- Set oidc.client_secret to the IdP client's secret, or oidc.client_secret_path to a readable file containing it
- If using client_secret_path, verify the key spelling and that the file exists and is readable by headscale
- Never set both keys (see the mutually-exclusive error)
- Restart headscale after the fix
Example fix
# before oidc: issuer: https://idp.example.com client_id: headscale # after oidc: issuer: https://idp.example.com client_id: headscale client_secret_path: /run/secrets/oidc-client-secret
Defensive patterns
Strategy: validation
Validate before calling
if cfg.OIDC.Issuer != "" && cfg.OIDC.ClientSecret == "" && cfg.OIDC.ClientSecretPath == "" {
return errors.New("client_secret or client_secret_path required")
} Type guard
null
Try / catch
null
Prevention
- Verify the secret file path is spelled client_secret_path and is readable
- Use config linting to catch missing OIDC secret keys before deploy
When it happens
Trigger: Configuring oidc.issuer and client_id without any secret; setting client_secret_path to a path but the key name misspelled so viper reads it as unset; empty-string secret from templating.
Common situations: Incomplete first-time OIDC setups; secret paths mounted but the config key typed as client_secret_file; environment-specific overlays forgetting the secret key.
Related errors
- oidc_client_secret and oidc_client_secret_path are mutually
- oidc.client_id is required when oidc.issuer is set
- pkce.method must be either 'plain' or 'S256'
- path cannot be empty
- busy_timeout must be >= 0
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/77512ba03da1c805.
Report an issue: GitHub.