{"record":{"id":"1623e39e8f3d8975","repo":"alibaba/spring-cloud-alibaba","slug":"already-acknowledged","errorCode":null,"errorMessage":"Already acknowledged","messagePattern":"Already acknowledged","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"warning","filePath":"spring-cloud-alibaba-starters/spring-cloud-starter-stream-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/integration/inbound/pull/RocketMQAckCallback.java","lineNumber":76,"sourceCode":"\tpublic boolean isAcknowledged() {\n\t\treturn this.acknowledged;\n\t}\n\n\t@Override\n\tpublic void noAutoAck() {\n\t\tthis.autoAckEnabled = false;\n\t}\n\n\t@Override\n\tpublic boolean isAutoAck() {\n\t\treturn this.autoAckEnabled;\n\t}\n\n\t@Override\n\tpublic void acknowledge(Status status) {\n\t\tAssert.notNull(status, \"'status' cannot be null\");\n\t\tif (this.acknowledged) {\n\t\t\tthrow new IllegalStateException(\"Already acknowledged\");\n\t\t}\n\t\tsynchronized (messageQueue) {\n\t\t\ttry {\n\t\t\t\tlong offset = messageExt.getQueueOffset();\n\t\t\t\tswitch (status) {\n\t\t\t\tcase REJECT, ACCEPT -> consumer.commit(Collections.singleton(messageQueue), false);\n\t\t\t\tcase REQUEUE -> consumer.seek(messageQueue, offset);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (MQClientException e) {\n\t\t\t\tthrow new IllegalStateException(e);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tthis.acknowledged = true;\n\t\t\t}\n\t\t}\n\t}\n","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/alibaba/spring-cloud-alibaba/blob/115d5901102009492e05d5ec18c3f79cad4077d0/spring-cloud-alibaba-starters/spring-cloud-starter-stream-rocketmq/src/main/java/com/alibaba/cloud/stream/binder/rocketmq/integration/inbound/pull/RocketMQAckCallback.java#L58-L94","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call acknowledge() exactly once per message; track ack ownership in one place.","If using auto-ack, do not also manually ack.","Guard re-entrancy in error/retry paths so the same callback is not acked again."],"exampleFix":"// before\nackCallback.acknowledge(Status.ACCEPT);\n// ...\nackCallback.acknowledge(Status.ACCEPT); // second call -> [127]\n// after\nackCallback.acknowledge(Status.ACCEPT);\n// honor isAcknowledged() before any further ack","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// Type guard: only ack if not yet acknowledged.\nif (!ackCallback.isAcknowledged()) {\n    ackCallback.acknowledge(Status.ACCEPT);\n} else {\n    log.warn(\"Skipping duplicate ack for message offset {}\", offset);\n}","tryCatchPattern":null,"preventionTips":["Acknowledge each message exactly once.","Avoid mixing manual and auto ack on the same consumer.","Centralize ack logic to prevent error/success double-ack."],"tags":["rocketmq","ack","consumer","pull","programming-error"],"backgroundTag":null,"analyzedSha":"115d5901102009492e05d5ec18c3f79cad4077d0","analyzedAt":"2026-08-14T04:47:13.900Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}