juanfont/headscale · error · errInvalidPKCEMethod
pkce.method must be either 'plain' or 'S256'
Error message
pkce.method must be either 'plain' or 'S256'
What it means
errInvalidPKCEMethod is an unexported config validation error in hscontrol/types/config.go:41 returned at config.go:363 when oidc.pkce.method is set to anything other than 'plain' or 'S256' (the PKCEMethodPlain/PKCEMethodS256 constants). The same concept has a runtime twin in hscontrol/oidc.go:58 used when an OIDC flow gets an unsupported method at request time.
Source
Thrown at hscontrol/types/config.go:41
"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 (
PolicyModeDB = "database"
PolicyModeFile = "file"
)View on GitHub (pinned to 565fd254d0)
Solutions
- Set oidc.pkce.method to 'S256' (recommended, widely supported)
- Use 'plain' only if the IdP lacks S256 support
- Match the exact casing and spelling; no other values are accepted
- Restart headscale after correcting the config
Example fix
# before
oidc:
pkce:
method: s256
# after
oidc:
pkce:
method: S256 Defensive patterns
Strategy: validation
Validate before calling
valid := map[string]bool{"plain": true, "S256": true}
if !valid[cfg.OIDC.PKCE.Method] {
return errors.New("pkce.method must be plain or S256")
} Type guard
null
Try / catch
null
Prevention
- Default to S256 and match spelling/casing exactly
- Ling config templates for lowercase 's256' typos before deploying
When it happens
Trigger: Setting oidc.pkce.method to values like 's256', 'SHA256', 'none', or leaving typos in the config; validation runs at config load (config.go:363) and fails startup; covered by config_test.go:444.
Common situations: IdP docs using different casing; copy-pasting from examples with 's256' lowercase; uncommenting a placeholder pkce block without editing it.
Related errors
- oidc.client_id is required when oidc.issuer is set
- oidc.client_secret or oidc.client_secret_path is required wh
- errInvalidPKCEMethod
- path cannot be empty
- busy_timeout must be >= 0
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/e308d66b5403e798.
Report an issue: GitHub.