prestodb/presto · error · IllegalArgumentException
nanos must be in range [0, 999_999_999]:
Error message
nanos must be in range [0, 999_999_999]:
What it means
fromEpochComponents validates that the nanos fraction is a sub-second value in [0, 999_999_999]. Values outside that range cannot represent a fractional-second component and would corrupt the packed timestamp, so IllegalArgumentException is thrown.
Source
Thrown at presto-common/src/main/java/com/facebook/presto/common/type/TimestampType.java:204
// Supported for all short precisions (p=0 through p=6, i.e. isShort()==true).
// Long precisions (p=7-12) require a 128-bit representation and are not yet supported.
public long toEpochMicros(long timestamp)
{
if (!isShort()) {
throw new UnsupportedOperationException(
"toEpochMicros is not supported for TIMESTAMP(" + precision + ")");
}
return getEpochSecond(timestamp) * 1_000_000L + getNanos(timestamp) / 1_000;
}
public long fromEpochComponents(long epochSecond, int nanos)
{
if (!isShort()) {
throw new UnsupportedOperationException(
"fromEpochComponents is not supported for TIMESTAMP(" + precision + ")");
}
if (nanos < 0 || nanos >= 1_000_000_000) {
throw new IllegalArgumentException("nanos must be in range [0, 999_999_999]: " + nanos);
}
long scale = PRECISION_SCALE[precision];
return epochSecond * scale + nanos / (1_000_000_000L / scale);
}
private static TimeUnit toTimeUnit(int precision)
{
// Exact-precision checks: DEFAULT_PRECISION (3) stores epoch-millis; MAX_SHORT_PRECISION (6)
// stores epoch-micros. Other precisions have no direct TimeUnit mapping.
if (precision == DEFAULT_PRECISION) {
return MILLISECONDS;
}
if (precision == MAX_SHORT_PRECISION) {
return MICROSECONDS;
}
throw new UnsupportedOperationException(
"Unsupported precision for TimeUnit conversion: TIMESTAMP(" + precision + ")");
}View on GitHub (pinned to 55bb57d202)
Solutions
- Normalize the components: fold nanos >= 1e9 into epochSecond (epochSecond += nanos / 1e9; nanos %= 1e9) before calling.
- Scale other units (ms, us) to nanoseconds before passing them.
- Validate/normalize inputs with Math.floorDiv / Math.floorMod to keep them in range.
Example fix
// before long ts = type.fromEpochComponents(epochSecond, nanos); // after epochSecond += Math.floorDiv(nanos, 1_000_000_000); nanos = Math.floorMod(nanos, 1_000_000_000); long ts = type.fromEpochComponents(epochSecond, nanos);
Defensive patterns
Strategy: validation
Validate before calling
if (nanos < 0 || nanos >= 1_000_000_000) {
epochSecond += Math.floorDiv(nanos, 1_000_000_000);
nanos = Math.floorMod(nanos, 1_000_000_000);
} Type guard
boolean isValidNanos(int nanos) { return nanos >= 0 && nanos < 1_000_000_000; } Try / catch
try {
packed = type.fromEpochComponents(epochSecond, nanos);
} catch (IllegalArgumentException e) {
packed = type.fromEpochComponents(
epochSecond + Math.floorDiv(nanos, 1_000_000_000),
Math.floorMod(nanos, 1_000_000_000));
} Prevention
- Always normalize fractional seconds with floorDiv/floorMod before composing timestamps.
- Convert ms/us inputs to nanoseconds with explicit scaling constants.
- Add boundary tests for nanos = -1, 0, 999_999_999, and 1_000_000_000.
When it happens
Trigger: Calling fromEpochComponents(epochSecond, nanos) with nanos negative (e.g. -1) or >= 1_000_000_000 (e.g. 1_000_000_000, meaning the carry belongs in epochSecond).
Common situations: Code that carries overflow from fractional arithmetic into nanos instead of into the seconds field; unit conversions passing milliseconds/microseconds without scaling to nanoseconds.
Related errors
- dictionarySourceIds must be the same
- blocks is empty
- Column does not have same position count (%s) as page (%s)
- QualifiedObjectName should have exactly 3 parts, found %s: %
- %s is not lowercase: %s
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/e05bca22a6241fb2.
Report an issue: GitHub.