nathanmarz/storm · warning

Failed to get metadata for a transaction

Error message

Failed to get metadata for a transaction

What it means

TransactionalSpoutCoordinator.sync() catches FailedException thrown while obtaining transaction metadata from the coordinator and logs 'Failed to get metadata for a transaction'. Unlike the batch executor, it only logs — no tuple is failed — so sync aborts for this pass and the coordinator simply retries on the next nextTuple/ack/fail driven sync. A persistent underlying failure means no new transaction batches are emitted.

Solutions

  1. Inspect the logged FailedException stack trace to find the metadata source that failed (usually ZooKeeper or a custom store).
  2. Restore/verify the metadata backend (ZK quorum health, connectivity, permissions).
  3. Add defensive retry/backoff in getMetadata for transient backend errors so sync can recover on its own.
  4. Check the coordinator's state in ZooKeeper for corruption; reset transaction state if safe.
  5. If failures persist, restart the coordinator task / topology after fixing the backend.

Example fix

// before
public Map<String, Object> getMetadata(TransactionAttempt tx) {
    return zkClient.readData(path); // transient ZK error -> FailedException each sync
}
// after
public Map<String, Object> getMetadata(TransactionAttempt tx) {
    for (int i = 0; i < 3; i++) {
        try { return zkClient.readData(path); }
        catch (Exception e) { sleep(backoff(i)); }
    }
    throw new FailedException("metadata unavailable after retries");
}
Defensive patterns

Strategy: retry

Validate before calling

// before relying on coordinator metadata, verify the backend is reachable
if (!zkClientExists(coordinatorStatePath)) { throw new IllegalStateException("coordinator metadata store unavailable: " + coordinatorStatePath); }

Try / catch

public Map<String,Object> getMetadata(TransactionAttempt tx) {
    try {
        return readMetadata(tx);
    } catch (TransientBackendException e) {
        throw new FailedException(e); // sync() logs and retries on next nextTuple/ack/fail
    }
}

Prevention

When it happens

Trigger: The coordinator's getMetadata/initializeTransaction path throws FailedException (e.g. metadata store read failure) while sync() is advancing _currTransaction and emitting TRANSACTION_BATCH_STREAM tuples; called from nextTuple, ack, or fail.

Common situations: Zookeeper or external metadata store unreachable; coordinator code throwing FailedException for transient backend errors; network partition between the coordinator task and its metadata backend; corrupted coordinator state causing repeated metadata fetch failures.

Related errors


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

Appendix: source

Thrown at storm-core/src/jvm/backtype/storm/transactional/TransactionalSpoutCoordinator.java:160

            _collector.emit(TRANSACTION_COMMIT_STREAM_ID, new Values(maybeCommit.attempt), maybeCommit.attempt);
        }
        
        try {
            if(_activeTx.size() < _maxTransactionActive) {
                BigInteger curr = _currTransaction;
                for(int i=0; i<_maxTransactionActive; i++) {
                    if((_coordinatorState.hasCache(curr) || _coordinator.isReady())
                            && !_activeTx.containsKey(curr)) {
                        TransactionAttempt attempt = new TransactionAttempt(curr, _rand.nextLong());
                        Object state = _coordinatorState.getState(curr, _initializer);
                        _activeTx.put(curr, new TransactionStatus(attempt));
                        _collector.emit(TRANSACTION_BATCH_STREAM_ID, new Values(attempt, state, previousTransactionId(_currTransaction)), attempt);
                    }
                    curr = nextTransactionId(curr);
                }
            }     
        } catch(FailedException e) {
            LOG.warn("Failed to get metadata for a transaction", e);
        }
    }

    @Override
    public Map<String, Object> getComponentConfiguration() {
        Config ret = new Config();
        ret.setMaxTaskParallelism(1);
        return ret;
    }
    
    private static enum AttemptStatus {
        PROCESSING,
        PROCESSED,
        COMMITTING
    }
    
    private static class TransactionStatus {
        TransactionAttempt attempt;

View on GitHub (pinned to cdb116e942)