apache/druid · error · IllegalStateException

No such sink

Error message

No such sink: %s

What it means

StreamAppenderator.getRowCount returns the in-memory row count of the sink for the given segment identifier and throws ISE if no such sink exists in the appenderator's sinks map. It is a programmer/API misuse signal: asking for metrics of a segment this appenderator never managed.

Solutions

  1. Track identifiers returned by add/append and only query row counts for live segments.
  2. Handle handoff: drop the identifier after push/persist indicates the sink is gone.
  3. Use getMetrics() or the appenderator's metrics emitter instead of direct sink lookups when unsure.

Example fix

// before
int rows = appenderator.getRowCount(identifier); // may not exist

// after
if (appenderator.getSinks().containsKey(identifier)) {
  int rows = appenderator.getRowCount(identifier);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!appenderator.getSinks().containsKey(identifier)) { return 0; } // or skip metric reporting

Try / catch

try { return appenderator.getRowCount(id) } catch (IllegalStateException e) { if (e.getMessage().startsWith("No such sink")) { return -1; } throw e; }

Prevention

When it happens

Trigger: Calling getRowCount with a SegmentIdWithShardSpec that was never added, or after the sink was dropped/handed off (removed from the sinks map).

Common situations: Custom ingestion frameworks querying row counts after segment handoff; identifier serialization/deserialization mismatches changing shard spec hash codes.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/cf97f916a765cb31. Report an issue: GitHub.

Appendix: source

Thrown at server/src/main/java/org/apache/druid/segment/realtime/appenderator/StreamAppenderator.java:472

        isPersistRequired = true;
      }
    }
    return new AppenderatorAddResult(identifier, sink.getNumRows(), isPersistRequired);
  }

  @Override
  public List<SegmentIdWithShardSpec> getSegments()
  {
    return ImmutableList.copyOf(sinks.keySet());
  }

  @Override
  public int getRowCount(final SegmentIdWithShardSpec identifier)
  {
    final Sink sink = sinks.get(identifier);

    if (sink == null) {
      throw new ISE("No such sink: %s", identifier);
    } else {
      return sink.getNumRows();
    }
  }

  @Override
  public int getTotalRowCount()
  {
    return totalRows.get();
  }

  @VisibleForTesting
  int getRowsInMemory()
  {
    return rowsCurrentlyInMemory.get();
  }

  @VisibleForTesting

View on GitHub (pinned to 9b90983fd2)