jaegertracing/jaeger · error

malformed token: multiple tokens found

Error message

malformed token: multiple tokens found

What it means

ValidTokenFromGRPCMetadata returns this error when the gRPC metadata contains more than one value under the authorization/bearer header. A single bearer token is expected; multiple values indicate a malformed or conflicting auth header set.

Source

Thrown at internal/auth/bearertoken/grpc.go:37

}

func (tss *tokenatedServerStream) Context() context.Context {
	return tss.context
}

// extract bearer token from the metadata
func ValidTokenFromGRPCMetadata(ctx context.Context, bearerHeader string) (string, error) {
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return "", nil
	}

	tokens := md.Get(bearerHeader)
	if len(tokens) < 1 {
		return "", nil
	}
	if len(tokens) > 1 {
		return "", errors.New("malformed token: multiple tokens found")
	}
	return tokens[0], nil
}

// NewStreamServerInterceptor creates a new stream interceptor that injects the bearer token into the context if available.
func NewStreamServerInterceptor() grpc.StreamServerInterceptor {
	return func(srv any, ss grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
		if token, _ := GetBearerToken(ss.Context()); token != "" {
			return handler(srv, ss)
		}

		bearerToken, err := ValidTokenFromGRPCMetadata(ss.Context(), Key)
		if err != nil {
			return err
		}

		return handler(srv, &tokenatedServerStream{
			ServerStream: ss,

View on GitHub (pinned to 806f444784)

Solutions

  1. Ensure the client sets the authorization metadata key exactly once per request (overwrite, not append)
  2. Fix gateway/proxy config that duplicates the Authorization header into gRPC metadata
  3. Replace with `md.Set(bearerHeader, token)` instead of `md.Append` when injecting tokens in interceptors

Example fix

// before
md.Append("authorization", "Bearer "+token)
// after
md.Set("authorization", "Bearer "+token)
Defensive patterns

Strategy: validation

Validate before calling

md, _ := metadata.FromIncomingContext(ctx)
vals := md.Get("authorization")
if len(vals) > 1 {
    return errors.New("authorization metadata set more than once")
}

Type guard

func singleToken(md metadata.MD, key string) (string, bool) {
    v := md.Get(key)
    if len(v) != 1 {
        return "", false
    }
    return v[0], true
}

Try / catch

token, err := bearertoken.ValidTokenFromGRPCMetadata(ctx)
if err != nil {
    if strings.Contains(err.Error(), "multiple tokens found") {
        return status.Error(codes.InvalidArgument, "duplicate authorization metadata")
    }
    return status.Error(codes.Unauthenticated, err.Error())
}

Prevention

When it happens

Trigger: A client (or proxy/metadata forwarding) sends the bearerHeader metadata key twice, e.g. attaching authorization metadata both manually and via an interceptor; md.Get returns len(tokens) > 1 and the function errors.

Common situations: Composed interceptors that each inject the token into the same metadata key; HTTP-to-gRPC gateways duplicating the Authorization header into metadata; middleware that appends instead of replacing.

Understand the failure class

Related errors


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/68fc6355786140e1. Report an issue: GitHub.