pinpoint-apm/pinpoint · warning
Command complete before establishment
Error message
Command complete before establishment
What it means
The collector's command stream onCompleted callback fires when the client half-closes the stream. If the connection (GrpcAgentConnection) was never established/registered before completion, the handler logs 'Command complete before establishment'. Unlike the other errors in this class this is only a server-side warning log; no gRPC error is sent and no exception is thrown to the caller.
Solutions
- Check the agent logs for why the command stream was closed immediately after connecting (crash, OOM, config error)
- Ensure the agent stays connected long enough for handshake/registration; fix any rapid reconnect loop
- Ignore this warning if it coincides with normal agent shutdown or collector redeploy; it is informational for stray completes
- If a custom client opens the stream, keep it open and complete it only after the session is finished
Defensive patterns
Strategy: try-catch
Try / catch
// this is a server-side warning, not a thrown error; guard the lifecycle instead
serverCallStreamObserver.setOnCancelHandler(() -> {
GrpcAgentConnection conn = connRef.get();
if (conn != null) { // handles the 'not established' case gracefully
agentConnectionRepository.remove(conn);
}
}); Prevention
- Investigate agents that open and immediately close the command stream (crash loops, bad config)
- Treat this warning as benign during deploys/restarts; alert only when it fires at high rate
- Keep command streams open for the agent session lifetime; complete them only on shutdown
- Use exponential backoff on agent reconnects to avoid registration/completion races
When it happens
Trigger: The agent closes/completes its command stream (or the call ends) before the request observer registered the GrpcAgentConnection in the connection repository, e.g. the stream is completed immediately after handleCommand/handleCommandV2 returns or the registration raced with shutdown.
Common situations: Agent connecting and instantly disconnecting (crash loop, deploy restart); a health-check or misconfigured client opening and closing the stream without sending messages; network flaps during collector restart where close events arrive before registration completes.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Failed to request. header=
- Failed to request. header=
- Failed to request. header=
- INVALID_ARGUMENT
- list size not same
AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07).
Data as JSON: /api/errors/0e423d9bae6cb474.
Report an issue: GitHub.
Appendix: source
Thrown at realtime/realtime-collector/src/main/java/com/navercorp/pinpoint/realtime/collector/receiver/grpc/GrpcCommandService.java:263
}
private void handleOnError(Throwable t, GrpcAgentConnection conn) {
if (conn == null) {
logger.warn("Command error before establishment");
return;
}
final Status status = Status.fromThrowable(t);
Metadata metadata = Status.trailersFromThrowable(t);
logger.info("Failed to command stream, {} => local, {} {}",
conn.getClusterKey(), status, metadata);
}
private void handleOnCompleted(GrpcAgentConnection conn) {
if (conn == null) {
logger.warn("Command complete before establishment");
return;
}
logger.info("{} => local. onCompleted", conn.getClusterKey());
}
@Override
public void commandEcho(PCmdEchoResponse response, StreamObserver<Empty> responseObserver) {
long sinkId = response.getCommonResponse().getResponseId();
final EchoPublisher publisher = this.echoSinkRepo.get(sinkId);
emitMono(response, responseObserver, publisher);
this.echoSinkRepo.invalidate(sinkId);
}
@Override
public void commandActiveThreadDump(PCmdActiveThreadDumpRes response, StreamObserver<Empty> responseObserver) {
long sinkId = response.getCommonResponse().getResponseId();
final ActiveThreadDumpPublisher publisher = this.activeThreadDumpSinkRepo.get(sinkId);View on GitHub (pinned to 744c3d3075)