go-kit/kit · error · ErrTokenMalformed

JWT is malformed

Error message

JWT is malformed

What it means

Returned by jwt.NewParser when the underlying jwt.ValidationError has the ValidationErrorMalformed bit set: the string pulled from the context is not parseable as a JWT at all. A valid JWT is three dot-separated base64url segments; anything else fails immediately.

Source

Thrown at auth/jwt/middleware.go:39

	// JWTClaimsContextKey holds the key used to store the JWT Claims in the
	// context.
	JWTClaimsContextKey contextKey = "JWTClaims"
)

var (
	// ErrTokenContextMissing denotes a token was not passed into the parsing
	// middleware's context.
	ErrTokenContextMissing = errors.New("token up for parsing was not passed through the context")

	// ErrTokenInvalid denotes a token was not able to be validated.
	ErrTokenInvalid = errors.New("JWT was invalid")

	// ErrTokenExpired denotes a token's expire header (exp) has since passed.
	ErrTokenExpired = errors.New("JWT is expired")

	// ErrTokenMalformed denotes a token was not formatted as a JWT.
	ErrTokenMalformed = errors.New("JWT is malformed")

	// ErrTokenNotActive denotes a token's not before header (nbf) is in the
	// future.
	ErrTokenNotActive = errors.New("token is not valid yet")

	// ErrUnexpectedSigningMethod denotes a token was signed with an unexpected
	// signing method.
	ErrUnexpectedSigningMethod = errors.New("unexpected signing method")
)

// NewSigner creates a new JWT generating middleware, specifying key ID,
// signing string, signing method and the claims you would like it to contain.
// Tokens are signed with a Key ID header (kid) which is useful for determining
// the key to use for parsing. Particularly useful for clients.
func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims jwt.Claims) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			token := jwt.NewWithClaims(method, claims)

View on GitHub (pinned to 78fbbceece)

Solutions

  1. Strip the auth scheme before storing: strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
  2. Reject/401 early when the header is empty or lacks the Bearer scheme so garbage never reaches the parser
  3. Verify the token has the shape header.payload.signature (two dots, non-empty segments) before invoking the endpoint
  4. Regenerate/copy the token cleanly if it was truncated or mangled

Example fix

// before: whole header, "Bearer " included, stored as the token
return context.WithValue(ctx, jwt.JWTContextKey, r.Header.Get("Authorization"))

// after: store only the bare token
v := strings.TrimSpace(r.Header.Get("Authorization"))
v = strings.TrimPrefix(v, "Bearer ")
if v == "" {
	return ctx // parser will surface the missing/malformed error as 401
}
return context.WithValue(ctx, jwt.JWTContextKey, v)
Defensive patterns

Strategy: try-catch

Validate before calling

func looksLikeJWT(s string) bool {
	parts := strings.Split(s, ".")
	return len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != ""
}
// apply to the value before storing it in jwt.JWTContextKey

Type guard

null

Try / catch

if _, err := ep(ctx, req); err != nil {
	if errors.Is(err, jwt.ErrTokenMalformed) {
		// 400/401: the client is not sending a real JWT — fix the sender, no retry
	}
}

Prevention

When it happens

Trigger: Storing the entire Authorization header, including the "Bearer " prefix, in jwt.JWTContextKey instead of the bare token; an empty string when the header is missing; a token truncated by logging/truncation or mangled by URL encoding; garbage values from a misconfigured proxy or a hand-built test context.

Common situations: RequestFunc forgets strings.TrimPrefix(header, "Bearer "); clients sending a raw API key or session id where a JWT is expected; copy-paste of tokens with newlines/quotes from terminal; gateways rewriting or double-encoding the Authorization header.

Understand the failure class

Related errors


AI-assisted analysis of go-kit/kit@78fbbceece (2026-08-15). Data as JSON: /api/errors/910a21744d53a027. Report an issue: GitHub.