nathanmarz/storm · error · RuntimeException

Results size is different than argument size:

Error message

Results size is different than argument size: 

What it means

StateQueryProcessor.finishBatch calls the QueryFunction's batchRetrieve and requires it to return exactly one result per input argument tuple. If results.size() != tuples.size(), it throws this RuntimeException naming the two sizes. This enforces the QueryFunction contract that batchRetrieve returns aligned, one-to-one results for every requested key.

Solutions

  1. Fix your QueryFunction.batchRetrieve to return exactly one entry per input argument — add null (or a sentinel) for keys not found.
  2. Check the State implementation's multiGet/batchRetrieve for filtering behavior; wrap it to pad missing results to null.
  3. Verify no deduplication/sorting is applied to the results list before returning.
  4. Log results.size() vs state.tuples.size() inside the QueryFunction to find which tuples are dropped.

Example fix

// before
public List<Object> batchRetrieve(State state, List<TridentTuple> args) {
    return store.multiGet(keys).stream().filter(Objects::nonNull).collect(toList());
}
// after: keep positional alignment, null for misses
List<Object> raw = store.multiGet(keys);
List<Object> results = new ArrayList<>(args.size());
for (Object r : raw) results.add(r == null ? null : r);
return results;
Defensive patterns

Strategy: try-catch

Validate before calling

// Java: inside your QueryFunction, assert alignment before returning
if (results.size() != args.size()) {
    throw new IllegalStateException("batchRetrieve must return 1 result per arg; got " + results.size() + " for " + args.size());
}

Try / catch

try { processorContextNext(node); } catch (RuntimeException e) { if (e.getMessage().startsWith("Results size is different than argument size")) { log.error("QueryFunction returned misaligned batch: {}", e.getMessage()); } throw e; }

Prevention

When it happens

Trigger: A custom QueryFunction's batchRetrieve returns fewer (or more) results than the number of argument tuples — e.g. skipping tuples whose key was not found in the State, returning null-less filtered lists, or a State implementation returning partial results.

Common situations: Custom State/BackingMap implementations that drop missing keys instead of returning null placeholders; caching layers returning only hits; DRPC state queries against a state store with inconsistent batch retrieval semantics.

Related errors


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

Appendix: source

Thrown at storm-core/src/jvm/storm/trident/planner/processor/StateQueryProcessor.java:86

    @Override
    public void startBatch(ProcessorContext processorContext) {
        processorContext.state[_context.getStateIndex()] =  new BatchState();
    }

    @Override
    public void execute(ProcessorContext processorContext, String streamId, TridentTuple tuple) {
        BatchState state = (BatchState) processorContext.state[_context.getStateIndex()];
        state.tuples.add(tuple);
        state.args.add(_projection.create(tuple));
    }

    @Override
    public void finishBatch(ProcessorContext processorContext) {
        BatchState state = (BatchState) processorContext.state[_context.getStateIndex()];
        if(!state.tuples.isEmpty()) {
            List<Object> results = _function.batchRetrieve(_state, state.args);
            if(results.size()!=state.tuples.size()) {
                throw new RuntimeException("Results size is different than argument size: " + results.size() + " vs " + state.tuples.size());
            }
            for(int i=0; i<state.tuples.size(); i++) {
                TridentTuple tuple = state.tuples.get(i);
                Object result = results.get(i);
                _collector.setContext(processorContext, tuple);
                _function.execute(_projection.create(tuple), result, _collector);            
            }
        }
    }
    
    private static class BatchState {
        public List<TridentTuple> tuples = new ArrayList<TridentTuple>();
        public List<TridentTuple> args = new ArrayList<TridentTuple>();
    }

    @Override
    public Factory getOutputFactory() {
        return _collector.getOutputFactory();

View on GitHub (pinned to cdb116e942)