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

  1. 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.
  2. Fix or filter the malformed records upstream so the exception never occurs.
  3. 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

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


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/1d3e6e0372d3a013. Report an issue: GitHub.