apache/rocketmq · error · AuthenticationException

username cannot be null.

Error message

username cannot be null.

What it means

The authentication context reached the handler but context.getUsername() is empty. The username is only set by the builder when a 'Credential=<user>' pair exists in the authorization header, so this means the client authenticated without presenting a credential - typically because the authorization header was absent, so the builder returned an early, empty context.

Source

Thrown at auth/src/main/java/org/apache/rocketmq/auth/authentication/chain/DefaultAuthenticationHandler.java:53

    private final AuthenticationMetadataProvider authenticationMetadataProvider;

    public DefaultAuthenticationHandler(AuthConfig config, Supplier<?> metadataService) {
        this.authenticationMetadataProvider = AuthenticationFactory.getMetadataProvider(config, metadataService);
    }

    @Override
    public CompletableFuture<Void> handle(DefaultAuthenticationContext context,
        HandlerChain<DefaultAuthenticationContext, CompletableFuture<Void>> chain) {
        return getUser(context).thenAccept(user -> doAuthenticate(context, user));
    }

    protected CompletableFuture<User> getUser(DefaultAuthenticationContext context) {
        if (this.authenticationMetadataProvider == null) {
            throw new AuthenticationException("The authenticationMetadataProvider is not configured");
        }
        if (StringUtils.isEmpty(context.getUsername())) {
            throw new AuthenticationException("username cannot be null.");
        }
        return this.authenticationMetadataProvider.getUser(context.getUsername());
    }

    protected void doAuthenticate(DefaultAuthenticationContext context, User user) {
        if (user == null) {
            throw new AuthenticationException("User:{} is not found.", context.getUsername());
        }
        if (user.getUserStatus() == UserStatus.DISABLE) {
            throw new AuthenticationException("User:{} is disabled.", context.getUsername());
        }
        String signature = AclSigner.calSignature(context.getContent(), user.getPassword());
        if (context.getSignature() == null
            || !MessageDigest.isEqual(signature.getBytes(AclSigner.DEFAULT_CHARSET), context.getSignature().getBytes(AclSigner.DEFAULT_CHARSET))) {
            throw new AuthenticationException("check signature failed.");
        }
    }
}

View on GitHub (pinned to 293f588571)

Solutions

  1. Configure access credentials on the client so every request carries 'Credential=<username>' in the authorization metadata.
  2. If the request is intentionally anonymous (e.g. internal health checks), disable authentication for that access path or whitelist it, rather than sending unsigned requests to an authenticated listener.
  3. Verify the username field actually lands in the header: 'RocketMQ Credential=<user>,Signature=<sig>,DateTime=<dt>'.

Example fix

// before
ClientConfigurationBuilder.build(new ClientConfiguration()); // no credentials

// after
ClientConfiguration config = new ClientConfiguration();
config.setCredentialsProvider(new StaticSessionCredentialsProvider(accessKey, secretKey));
ClientConfigurationBuilder.build(config);
Defensive patterns

Strategy: validation

Validate before calling

// Client-side: refuse to send requests when credentials are unset
if (accessKey == null || accessKey.isEmpty()) {
    throw new IllegalStateException("credentials required: server has authentication enabled");
}

Type guard

boolean hasCredentials(ClientConfiguration c) { return c.getCredentialsProvider() != null && c.getCredentialsProvider().getCredentials() != null; }

Try / catch

catch (AuthenticationException e) { if message contains "username cannot be null" -> attach credentials provider and retry once; otherwise rethrow. }

Prevention

When it happens

Trigger: Client sends a gRPC request with no authorization metadata (or one lacking Credential=...), while the server has authentication enabled and routes the request through DefaultAuthenticationHandler. Also hit when the header parse skipped the credential pair.

Common situations: Client SDK built without setting credentials (empty accessKey/secretKey); a plain gRPC health-check or admin tool hitting an endpoint that requires auth; forgetting to attach the credentials interceptor.

Related errors


AI-assisted analysis of apache/rocketmq@293f588571 (2026-08-14). Data as JSON: /api/errors/0489c9ff2ed46f1c. Report an issue: GitHub.