nathanmarz/storm · error · RuntimeException

Acked a non-existent or already acked/failed id

Error message

Acked a non-existent or already acked/failed id: ${id}

What it means

ShellBolt.handleAck processes an "ack" command from the multilang subprocess: it removes the pending Tuple keyed by the message id and acks it. If the id is not in _inputs (never emitted with that id, or already acked/failed/removed), it throws this RuntimeException. The subprocess double-acked, acked an unknown id, or acked after a fail.

Solutions

  1. Ensure the subprocess acks each id exactly once — remove duplicate ack calls (e.g. ack in both try and finally)
  2. Ack only ids that were passed in the incoming tuple's message id, echoing them verbatim; do not generate ids in the script
  3. Check the script's fail/ack paths are mutually exclusive per id
  4. Review worker logs for the reader thread trace to identify which ids are duplicated and correlate with script logic

Example fix

# before (python bolt)
try:
    process(tup)
    storm.ack(tup)
except Exception:
    storm.fail(tup)
finally:
    storm.ack(tup)  # duplicate ack!

# after
try:
    process(tup)
    storm.ack(tup)
except Exception:
    storm.fail(tup)
Defensive patterns

Strategy: validation

Validate before calling

# inside the multilang bolt script — guard before acking:
acked = set()
def safe_ack(tup):
    tid = tup['id']
    if tid not in acked:
        acked.add(tid)
        storm.ack(tup)

Try / catch

// on the reading side (custom ShellBolt subclass or log watcher):
try {
    handleAck(action);
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Acked a non-existent")) {
        LOG.error("Multilang process double-acked id {}", action.get("id"));
        // treat as warning and continue, or restart the process
    } else throw e;
}

Prevention

When it happens

Trigger: The subprocess sends {"command":"ack","id":X} twice for the same id; it acks an id it invented instead of echoing the id from the tuple it received; it acks after the anchor was already failed; emitting without anchoring/ids so _inputs has no such entry.

Common situations: Custom Python/shell bolts with hand-rolled ack logic acking both on success and in a finally/error path; script caching tuple ids and re-acking on retry; race between handleFail and handleAck from the reader thread; upgrading the script while topology state persists.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of nathanmarz/storm@cdb116e942 (2026-09-12). Data as JSON: /api/errors/2f15dfc492db6666. Report an issue: GitHub.

Appendix: source

Thrown at storm-core/src/jvm/backtype/storm/task/ShellBolt.java:193

            obj.put("task", input.getSourceTask());
            obj.put("tuple", input.getValues());
            _pendingWrites.put(obj);
        } catch(InterruptedException e) {
            throw new RuntimeException("Error during multilang processing", e);
        }
    }

    public void cleanup() {
        _running = false;
        _process.destroy();
        _inputs.clear();
    }

    private void handleAck(Map action) {
        String id = (String) action.get("id");
        Tuple acked = _inputs.remove(id);
        if(acked==null) {
            throw new RuntimeException("Acked a non-existent or already acked/failed id: " + id);
        }
        _collector.ack(acked);
    }

    private void handleFail(Map action) {
        String id = (String) action.get("id");
        Tuple failed = _inputs.remove(id);
        if(failed==null) {
            throw new RuntimeException("Failed a non-existent or already acked/failed id: " + id);
        }
        _collector.fail(failed);
    }

    private void handleError(Map action) {
        String msg = (String) action.get("msg");
        _collector.reportError(new Exception("Shell Process Exception: " + msg));
    }

View on GitHub (pinned to cdb116e942)