apache/beam · error · RuntimeException
Unable to create prepared statement for type: ${type}
Error message
Unable to create prepared statement for type: ${type} What it means
Thrown by JdbcIo's statement parameter setter when a JDBC PreparedStatement setXxx call fails with a SQLException while binding a Beam schema row field to the SQL statement. The Beam FieldType is mapped to a typed caller via JdbcUtil.getPreparedStatementSetCaller(type); if the driver rejects the value for that column/type, this wrapper is raised with the offending type in the message.
Source
Thrown at sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/JdbcUtil.java:382
throws SQLException {
ps.setNull(i + 1, type.getVendorTypeNumber());
}
static class BeamRowPreparedStatementSetter implements JdbcIO.PreparedStatementSetter<Row> {
@Override
public void setParameters(Row row, PreparedStatement statement) {
Schema schema = row.getSchema();
List<Schema.Field> fieldTypes = schema.getFields();
IntStream.range(0, fieldTypes.size())
.forEachOrdered(
i -> {
Schema.FieldType type = fieldTypes.get(i).getType();
try {
JdbcUtil.getPreparedStatementSetCaller(type)
.set(row, statement, i, SchemaUtil.FieldWithIndex.of(schema.getField(i), i));
} catch (SQLException throwables) {
throwables.printStackTrace();
throw new RuntimeException(
String.format("Unable to create prepared statement for type: %s", type),
throwables);
}
});
}
}
private static JdbcIO.PreparedStatementSetCaller createBytesCaller() {
return (element, ps, i, fieldWithIndex) -> {
byte[] value = element.getBytes(fieldWithIndex.getIndex());
if (value != null) {
validateLogicalTypeLength(fieldWithIndex.getField(), value.length);
}
ps.setBytes(i + 1, value);
};
}
private static JdbcIO.PreparedStatementSetCaller createStringCaller() {View on GitHub (pinned to 12126d8942)
Solutions
- Compare the row's schema field types with the actual database table column definitions and widen/align the column (ALTER TABLE) or truncate/coerce the value in the pipeline
- Catch SQLException details in the chained cause (getCause()) to identify which field/value failed
- For string fields, pre-validate lengths in a DoFn before JdbcIo.write()
- Ensure the JDBC driver version matches the database server version to avoid binding quirks
Example fix
// before
rows.apply(JdbcIO.<Row>write().withDataSourceConfiguration(cfg).withStatement(INSERT));
// after: truncate oversized strings first
rows.apply(ParDo.of(new DoFn<Row, Row>() {
@ProcessElement public void process(ProcessContext c) {
Row r = c.element();
String v = r.getString(0);
c.element(Row.fromRow(r).withValue(0, v != null && v.length() > 255 ? v.substring(0, 255) : v).build());
}
})).apply(JdbcIO.<Row>write()...); Defensive patterns
Strategy: validation
Validate before calling
for (int i = 0; i < row.getSchema().getFieldCount(); i++) {
Schema.FieldType t = row.getSchema().getField(i).getType();
Object v = row.getValue(i);
if (v instanceof String && t.equals(Schema.FieldType.STRING)) {
// check against known column capacity
if (((String) v).length() > 255) throw new IllegalArgumentException("field " + i + " too long");
}
if (v instanceof Double && (Double.isInfinite((Double) v) || Double.isNaN((Double) v))) {
throw new IllegalArgumentException("field " + i + " is NaN/Inf, driver may reject it");
}
} Type guard
boolean isBindable(Object v) {
return v == null || v instanceof String || v instanceof Number || v instanceof Boolean
|| v instanceof java.sql.Timestamp || v instanceof java.sql.Date;
} Try / catch
try {
rows.apply(JdbcIO.<Row>write().withDataSourceConfiguration(cfg).withStatement(sql));
} catch (RuntimeException e) {
if (e.getMessage().startsWith("Unable to create prepared statement for type")) {
LOG.error("JDBC bind failed for type {}; cause: {}", e.getCause());
}
throw e;
} Prevention
- Align Beam schema field types with actual table DDL before writing
- Pre-truncate/validate string lengths against column capacities
- Avoid NaN/Infinity doubles for drivers that reject them
- Keep JDBC driver version in sync with the database server
When it happens
Trigger: Writing a Beam row to JDBC where row field i has a type whose value cannot be bound by the driver: e.g. a string longer than the column (CHAR/VARCHAR overflow), a NaN/infinite DOUBLE on drivers that reject it, or a logical-type value incompatible with the mapped setXxx call.
Common situations: Schema drift between Beam pipeline output and database table (column narrowed after pipeline was built); inserting oversized VARCHAR/DECIMAL values; PostgreSQL rejecting infinite/NaN doubles; inserting into a column with a mismatched precision.
Related errors
- Beam JDBC connection has not been initialized
- Unable to parse statement
- Unable to parse query %s
- Unable to convert query %s
- Index out of bounds for positional parameter: ${index}
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/f51bc8eb554b692b.
Report an issue: GitHub.