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

  1. Set oidc.pkce.method to 'S256' (recommended, widely supported)
  2. Use 'plain' only if the IdP lacks S256 support
  3. Match the exact casing and spelling; no other values are accepted
  4. 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

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


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/e308d66b5403e798. Report an issue: GitHub.