argoproj/argo-workflows · error

failed to create JWT encrypter: %w

Error message

failed to create JWT encrypter: %w

What it means

newSso derives an AES-256 key from the stored RSA key and builds a go-jose encrypter (A256GCM, DIRECT key management, DEFLATE compression) used to encrypt SSO session cookies. jose.NewEncrypter only fails on invalid algorithm/key combinations, so this error means the constructed JWE parameters were rejected — practically unreachable with the hardcoded constants, indicating a broken build or tampered dependency.

Source

Thrown at server/auth/sso/sso.go:237

	config := &oauth2.Config{
		ClientID:     string(clientID),
		ClientSecret: string(clientSecret),
		RedirectURL:  c.RedirectURL,
		Endpoint:     provider.Endpoint(),
		Scopes:       append(c.Scopes, oidc.ScopeOpenID),
	}
	idTokenVerifier := provider.Verifier(&oidc.Config{ClientID: config.ClientID})
	// The server both mints and verifies these tokens, so symmetric AEAD is
	// sufficient: encryption with A256GCM also authenticates, and go-jose v4
	// only permits encrypt-only JWTs with symmetric algorithms. Asymmetric
	// encryption needed a nested signature, which pushed the cookie over the
	// 4KB browser limit (https://github.com/argoproj/argo-workflows/issues/16744).
	// The AES key is derived from the RSA key already stored in the secret so
	// that existing installations don't need a secret migration.
	encryptionKey := sha256.Sum256(x509.MarshalPKCS1PrivateKey(privateKey))
	encrypter, err := jose.NewEncrypter(jose.A256GCM, jose.Recipient{Algorithm: jose.DIRECT, Key: encryptionKey[:]}, &jose.EncrypterOptions{Compression: jose.DEFLATE})
	if err != nil {
		return nil, fmt.Errorf("failed to create JWT encrypter: %w", err)
	}

	var filterGroupsRegex []*regexp.Regexp
	if len(c.FilterGroupsRegex) > 0 {
		for _, regex := range c.FilterGroupsRegex {
			compiledRegex, err := regexp.Compile(regex)
			if err != nil {
				return nil, fmt.Errorf("failed to compile sso.filterGroupRegex: %s %w", regex, err)
			}
			filterGroupsRegex = append(filterGroupsRegex, compiledRegex)
		}
	}

	lf := logging.Fields{"redirectUrl": config.RedirectURL, "logoutRedirectUrl": c.LogoutRedirectURL, "issuer": c.Issuer, "issuerAlias": "DISABLED", "clientId": c.ClientID, "scopes": config.Scopes, "insecureSkipVerify": c.InsecureSkipVerify, "filterGroupsRegex": c.FilterGroupsRegex, "rootCA": c.RootCA}
	if c.IssuerAlias != "" {
		lf["issuerAlias"] = c.IssuerAlias
	}
	logger := logging.RequireLoggerFromContext(ctx).WithFields(lf)

View on GitHub (pinned to 35bff19146)

Solutions

  1. Read the wrapped error — it states which JWE parameter was rejected
  2. Verify the vendored go-jose version matches go.mod (go mod verify / make vendor)
  3. Rebuild argo-server from a clean tree (make cli / official image)
  4. Report upstream if a supported go-jose version rejects A256GCM+DIRECT with a 32-byte key
Defensive patterns

Strategy: try-catch

Validate before calling

key := sha256.Sum256(x509.MarshalPKCS1PrivateKey(privKey))
if len(key) != 32 {
    return fmt.Errorf("derived encryption key must be 32 bytes for A256GCM/DIRECT")
}

Try / catch

if _, err := sso.New(ctx, cfg, secretsIf, baseHRef, secure); err != nil {
    if strings.Contains(err.Error(), "failed to create JWT encrypter") {
        return fmt.Errorf("go-jose rejected JWE parameters; verify vendored go-jose version: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: jose.NewEncrypter returning an error in New(); with jose.A256GCM + jose.DIRECT + a 32-byte key this should never happen unless the go-jose library version in the vendored dependency rejects this combination.

Common situations: Dependency upgrade/downgrade of github.com/go-jose/go-jose where DIRECT key management for A256GCM is disallowed or the key length check changed; custom builds with modified constants.

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/f4fe9bfd9fdb181e. Report an issue: GitHub.