alibaba/spring-cloud-alibaba · warning · IllegalStateException

Already acknowledged

Error message

Already acknowledged

What it means

RocketMQAckCallback.acknowledge throws IllegalStateException('Already acknowledged') if called more than once for the same message. After the first acknowledge the internal `acknowledged` flag is set true in the finally block; a second call is a programming error in the consuming flow.

Source

Thrown at spring-cloud-alibaba-starters/spring-cloud-starter-stream-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/integration/inbound/pull/RocketMQAckCallback.java:76

	public boolean isAcknowledged() {
		return this.acknowledged;
	}

	@Override
	public void noAutoAck() {
		this.autoAckEnabled = false;
	}

	@Override
	public boolean isAutoAck() {
		return this.autoAckEnabled;
	}

	@Override
	public void acknowledge(Status status) {
		Assert.notNull(status, "'status' cannot be null");
		if (this.acknowledged) {
			throw new IllegalStateException("Already acknowledged");
		}
		synchronized (messageQueue) {
			try {
				long offset = messageExt.getQueueOffset();
				switch (status) {
				case REJECT, ACCEPT -> consumer.commit(Collections.singleton(messageQueue), false);
				case REQUEUE -> consumer.seek(messageQueue, offset);
				}
			}
			catch (MQClientException e) {
				throw new IllegalStateException(e);
			}
			finally {
				this.acknowledged = true;
			}
		}
	}

View on GitHub (pinned to 115d590110)

Solutions

  1. Call acknowledge() exactly once per message; track ack ownership in one place.
  2. If using auto-ack, do not also manually ack.
  3. Guard re-entrancy in error/retry paths so the same callback is not acked again.

Example fix

// before
ackCallback.acknowledge(Status.ACCEPT);
// ...
ackCallback.acknowledge(Status.ACCEPT); // second call -> [127]
// after
ackCallback.acknowledge(Status.ACCEPT);
// honor isAcknowledged() before any further ack
Defensive patterns

Strategy: type-guard

Type guard

// Type guard: only ack if not yet acknowledged.
if (!ackCallback.isAcknowledged()) {
    ackCallback.acknowledge(Status.ACCEPT);
} else {
    log.warn("Skipping duplicate ack for message offset {}", offset);
}

Prevention

When it happens

Trigger: Application code (or a downstream handler) invokes AcknowledgmentCallback.acknowledge(...) twice on the same RocketMQAckCallback instance, or the same message is routed through two acking handlers.

Common situations: Manual ack in a try/finally plus framework auto-ack; replay logic re-acking; both error-path and success-path acking the same message; duplicate handler subscriptions.

Related errors


AI-assisted analysis of alibaba/spring-cloud-alibaba@115d590110 (2026-08-14). Data as JSON: /api/errors/1623e39e8f3d8975. Report an issue: GitHub.