apache/beam · critical · RuntimeException
Encountered Bad Record:
Error message
Encountered Bad Record:
What it means
BadRecordRouter.route throws this when a record fails processing AND the error handler is the default 'throw' handler (no BadRecordErrorHandler configured). The bad record's human-readable JSON (or a fallback string if serialization fails) is appended to the message and the RuntimeException fails the pipeline, which is Beam's fail-fast default for error handling.
Source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/errorhandling/BadRecordRouter.java:92
BoundedWindow window)
throws Exception {
route(record, exception);
}
private <RecordT> void route(RecordT record, @Nullable Exception exception) throws Exception {
if (exception != null) {
throw exception;
} else {
Preconditions.checkArgumentNotNull(record);
String encodedRecord =
BadRecord.Record.builder()
.addHumanReadableJson(record)
.build()
.getHumanReadableJsonRecord();
if (encodedRecord == null) {
encodedRecord = "Unable to serialize bad record";
}
throw new RuntimeException("Encountered Bad Record: " + encodedRecord);
}
}
}
class RecordingBadRecordRouter implements BadRecordRouter {
@Override
public <RecordT> void route(
MultiOutputReceiver outputReceiver,
RecordT record,
@Nullable Coder<RecordT> coder,
@Nullable Exception exception,
String description)
throws Exception {
outputReceiver
.get(BAD_RECORD_TAG)
.output(BadRecord.fromExceptionInformation(record, coder, exception, description));
}View on GitHub (pinned to 12126d8942)
Solutions
- Attach a BadRecordErrorHandler via the IO's error-handling API (e.g. .withExceptionHandling / ErrorHandling options) so bad records go to an error PCollection instead of throwing.
- Fix or filter the malformed records upstream so the exception never occurs.
- Inspect the record JSON in the message to identify the specific bad element and its parse failure.
Example fix
// before BigQueryIO.readTableRows().from(table) // throws on bad record // after ErrorHandle<BadRecord> errorHandler = ErrorHandler.getDefaultBadRecordErrorHandler(...); BigQueryIO.readTableRows().from(table).withBadRecordErrorHandler(errorHandler)
Defensive patterns
Strategy: validation
Validate before calling
// Before running, ensure an error handler is attached when the IO supports it:
boolean handlerAttached = io != null && io.getBadRecordErrorHandler() != null;
if (!handlerAttached) { throw new IllegalStateException("Bad records will fail the pipeline"); } Type guard
null
Try / catch
try {
pipeline.run().waitUntilFinish();
} catch (RuntimeException e) {
if (e.getMessage().startsWith("Encountered Bad Record:")) {
LOG.error("Bad record failed pipeline: {}", e.getMessage());
}
} Prevention
- Always configure a BadRecordErrorHandler for IOs reading untrusted/malformed input
- Validate input records upstream before pipeline execution
- Route bad records to a dead-letter PCollection in production pipelines
When it happens
Trigger: An exception is thrown while processing an element in a transform that supports bad-record routing (e.g. BigQueryIO, FileIO reads) and no .withErrorHandling(...) BadRecordErrorHandler was configured, or the configured handler's output was not wired, leaving the throwing router active.
Common situations: Malformed input records (bad JSON, wrong schema) in BigQuery Storage Read/Write or FileIO pipelines where developers expected errors to be routed to an error PCollection but forgot to attach the error handler.
Related errors
- Error collections cannot be added after Error Handler is clo
- ErrorHandler must be finalized before the output can be retu
- Error handler is already closed, and may not be closed twice
- No Op handler should not be closed. This implies this IO is
- No Op handler has no output. This implies this IO is misconf
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/1d3e6e0372d3a013.
Report an issue: GitHub.