apache/incubator-seata · error · IllegalArgumentException
serverMessageSender must not be null
Error message
serverMessageSender must not be null
What it means
DefaultServerMessageListenerImpl.getServerMessageSender() throws if remotingServer was never injected via setServerMessageSender. The listener needs the RemotingServer to push asynchronous responses/requests back to clients; using it before wiring is a bean-lifecycle or configuration error, not a runtime race.
Source
Thrown at core/src/main/java/org/apache/seata/core/rpc/DefaultServerMessageListenerImpl.java:224
ExecutorService mergeSendExecutorService = ThreadPoolExecutorFactory.newThreadPoolExecutor(
THREAD_PREFIX,
MAX_LOG_SEND_THREAD,
MAX_LOG_SEND_THREAD,
KEEP_ALIVE_TIME,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
true);
mergeSendExecutorService.submit(new BatchLogRunnable());
}
/**
* Gets server message sender.
*
* @return the server message sender
*/
public RemotingServer getServerMessageSender() {
if (remotingServer == null) {
throw new IllegalArgumentException("serverMessageSender must not be null");
}
return remotingServer;
}
/**
* Sets server message sender.
*
* @param remotingServer the remoting server
*/
public void setServerMessageSender(RemotingServer remotingServer) {
this.remotingServer = remotingServer;
}
/**
* The type Batch log runnable.
*/
static class BatchLogRunnable implements Runnable {
View on GitHub (pinned to e01f97c6db)
Solutions
- Ensure setServerMessageSender(remotingServer) is called during assembly before any message handling — in Spring, wire it as a required @Autowired setter or constructor arg.
- In tests, stub a RemotingServer and inject it in @BeforeEach.
- Prefer reusing the framework's server bootstrap rather than reassembling beans by hand.
Example fix
// before DefaultServerMessageListenerImpl l = new DefaultServerMessageListenerImpl(); l.getServerMessageSender().sendResponse(msg); // after RemotingServer stub = mock(RemotingServer.class); l.setServerMessageSender(stub); l.getServerMessageSender().sendResponse(msg);
Defensive patterns
Strategy: validation
Validate before calling
if (listener.getServerMessageSender() == null /* would throw */) {
throw new IllegalStateException("wire setServerMessageSender before use");
}
// simpler: inject via constructor/Spring so it cannot be missing
DefaultServerMessageListenerImpl l = new DefaultServerMessageListenerImpl();
l.setServerMessageSender(remotingServer); // required assembly step Try / catch
catch (IllegalArgumentException e) {
if (e.getMessage().contains("must not be null")) { throw new IllegalStateException("listener used before wiring", e); }
} Prevention
- Prefer constructor injection for mandatory collaborators
- Assert wiring in @PostValidate/@AfterPropertiesSet startup hooks
When it happens
Trigger: Manually constructing DefaultServerMessageListenerImpl (e.g. in tests or custom bootstrap) and calling send-back APIs without calling setServerMessageSender; or a Spring/@Bean configuration where the listener bean is created/resolved before the remoting server bean is wired into it.
Common situations: Custom server-side bootstrap replacing the default Seata server assembly; unit tests instantiating the listener directly; @Autowired ordering issues where an eager init consumes the listener pre-wiring.
Related errors
- targetClass must be not null
- Couldn't parser any Remoting info
- datasource required not null!
- applicationId: %s, txServiceGroup: %s
- bytes is null
AI-assisted analysis of apache/incubator-seata@e01f97c6db (2026-08-14).
Data as JSON: /api/errors/6ffebef05451ada1.
Report an issue: GitHub.