apache/beam · error · IllegalArgumentException
Unregistering consumer which was not registered.
Error message
Unregistering consumer which was not registered.
What it means
unregisterConsumer throws when the instruction id being removed has no pending (incomplete) future in the receivers map. Per the source, an incomplete future is only inserted by the inbound observer, so if remove() returns a done future or nothing the consumer was never properly registered (or was already fully consumed).
Source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/fn/data/BeamFnDataGrpcMultiplexer.java:140
throw new IllegalArgumentException("Instruction id was registered twice");
}
return existing;
}
if (poisonedInstructionIds.getIfPresent(instructionId) != null) {
throw new IllegalArgumentException("Instruction id was poisoned");
}
return CompletableFuture.completedFuture(receiver);
});
}
/** Unregisters a previously registered consumer. */
public void unregisterConsumer(String instructionId) {
@Nullable CompletableFuture<CloseableFnDataReceiver<BeamFnApi.Elements>> receiverFuture =
receivers.remove(instructionId);
if (receiverFuture != null && !receiverFuture.isDone()) {
// The future must have been inserted by the inbound observer since registerConsumer completes
// the future.
throw new IllegalArgumentException("Unregistering consumer which was not registered.");
}
}
/**
* Poisons an instruction id.
*
* <p>Any records for the instruction on the inbound observer will be dropped for the next {@link
* #POISONED_INSTRUCTION_ID_CACHE_TIMEOUT}.
*/
public void poisonInstructionId(String instructionId) {
poisonedInstructionIds.put(instructionId, Boolean.TRUE);
@Nullable CompletableFuture<CloseableFnDataReceiver<BeamFnApi.Elements>> receiverFuture =
receivers.remove(instructionId);
if (receiverFuture != null) {
// Completing exceptionally has no effect if the future was already notified. In that case
// whatever registered the receiver needs to handle cancelling it.
receiverFuture.completeExceptionally(new PoisonedException());
if (!receiverFuture.isCompletedExceptionally()) {View on GitHub (pinned to 12126d8942)
Solutions
- Ensure every unregisterConsumer call is paired one-to-one with a successful registerConsumer call for the same id.
- Guard cleanup with a flag so the id is unregistered only once.
- Catch IllegalArgumentException in teardown paths and log instead of failing shutdown.
Example fix
// before
multiplexer.unregisterConsumer(instructionId); // in finally, may run twice
// after
if (!unregistered.contains(instructionId)) {
unregistered.add(instructionId);
multiplexer.unregisterConsumer(instructionId);
} Defensive patterns
Strategy: try-catch
Validate before calling
// track registered ids locally boolean registered = registeredIds.contains(instructionId);
Try / catch
try { multiplexer.unregisterConsumer(id); } catch (IllegalArgumentException e) { LOG.debug("Already unregistered: " + id); } Prevention
- Pair register/unregister strictly one-to-one
- Use a boolean/flag to make teardown idempotent
- Never unregister in multiple finally blocks
When it happens
Trigger: Calling unregisterConsumer(instructionId) when registerConsumer never ran for that id, the instruction was already unregistered, or the future inserted by the inbound observer had already completed before the unregister call.
Common situations: Double-cleanup in a finally block; unregistering a receiver whose process bundle already finished; race between inbound observer completing the future and the teardown path.
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
- Instruction id was poisoned
- PoisonedException
- Illegal access to pipeline after visitor traversal was compl
- One or more ErrorHandlers aren't closed, and this pipeline c
- UnsupportedOperationException
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/04430f84da786da2.
Report an issue: GitHub.