apache/flink · error · IllegalArgumentException

Csv does not support TIME type with precision: %s, it only s

Error message

Csv does not support TIME type with precision: %s, it only supports precision 0 ~ 3.

What it means

CsvToRowDataConverters.convertToTime refuses TIME(p) columns with precision > 3: CSV parsing goes through LocalTime.parse and keeps only milliseconds (see FLINK-17525), so higher precisions would silently lose digits. The converter therefore fails fast with IllegalArgumentException while building the deserialization schema rather than corrupting data at runtime.

Source

Thrown at flink-formats/flink-csv/src/main/java/org/apache/flink/formats/csv/CsvToRowDataConverters.java:244

            // avoid redundant toString and parseDouble, for better performance
            return (float) jsonNode.asDouble();
        } else {
            return Float.parseFloat(jsonNode.asText().trim());
        }
    }

    private int convertToDate(JsonNode jsonNode) {
        // csv currently is using Date.valueOf() to parse date string
        return (int) Date.valueOf(jsonNode.asText()).toLocalDate().toEpochDay();
    }

    private CsvToRowDataConverter convertToTime(TimeType timeType) {
        final int precision = timeType.getPrecision();
        // csv currently is using Time.valueOf() to parse time string
        // TODO: FLINK-17525 support millisecond and nanosecond
        // get number of milliseconds of the day
        if (precision > 3) {
            throw new IllegalArgumentException(
                    "Csv does not support TIME type "
                            + "with precision: "
                            + precision
                            + ", it only supports precision 0 ~ 3.");
        }
        return jsonNode -> {
            LocalTime localTime = LocalTime.parse(jsonNode.asText());
            int mills = (int) (localTime.toNanoOfDay() / 1000_000L);
            // this is for rounding off values out of precision
            if (precision == 2) {
                mills = mills / 10 * 10;
            } else if (precision == 1) {
                mills = mills / 100 * 100;
            } else if (precision == 0) {
                mills = mills / 1000 * 1000;
            }
            return mills;
        };

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Change the column to TIME(3) or plain TIME (precision 0) if millisecond resolution is acceptable.
  2. Ingest the field as STRING and parse to a higher-precision type downstream (e.g. TO_TIMESTAMP_LSB or custom UDF).
  3. If micro/nano precision is a hard requirement, vote/track FLINK-17525 or use a format that supports it (avro/json with LONG micros).

Example fix

-- before
event_time TIME(6),

-- after
event_time TIME(3),  -- or: event_time STRING parsed downstream
Defensive patterns

Strategy: validation

Validate before calling

// Check every TIME column when format='csv':
for (Column c : table.getResolvedSchema().getColumns()) {
    if (c.getDataType().getLogicalType() instanceof TimeType
            && ((TimeType) c.getDataType().getLogicalType()).getPrecision() > 3) {
        throw new ValidationException("csv does not support TIME(>3): " + c.getName());
    }
}

Type guard

static boolean csvTimePrecisionOk(TimeType t) { return t.getPrecision() <= 3; }

Prevention

When it happens

Trigger: A csv-format table declaring TIME(4) through TIME(9); common when copying a TIME(6) column definition from another system (e.g. Oracle/MySQL TIME(6), or a JSON/Avro schema with micro precision).

Common situations: Porting DDLs from databases whose default TIME precision exceeds 3; upstream producers emitting microseconds in time fields; schema-import tooling that preserves source precision verbatim.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/1699a26728e022d9. Report an issue: GitHub.