prestodb/presto · error · IllegalArgumentException

Invalid year month interval qualifier: to

Error message

Invalid year month interval qualifier:  to 

What it means

DateTimeUtils.parseYearMonthInterval parses YEAR–MONTH range interval literals (e.g. INTERVAL '2-6' YEAR TO MONTH). Only YEAR, YEAR TO MONTH, and MONTH qualifiers are legal; any other start/end combination throws this IllegalArgumentException naming the invalid qualifier pair.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/util/DateTimeUtils.java:524

    }

    public static long parseYearMonthInterval(String value, IntervalField startField, Optional<IntervalField> endField)
    {
        IntervalField end = endField.orElse(startField);

        if (startField == IntervalField.YEAR && end == IntervalField.MONTH) {
            PeriodFormatter periodFormatter = INTERVAL_YEAR_MONTH_FORMATTER;
            return parsePeriodMonths(value, periodFormatter, startField, end);
        }
        if (startField == IntervalField.YEAR && end == IntervalField.YEAR) {
            return parsePeriodMonths(value, INTERVAL_YEAR_FORMATTER, startField, end);
        }

        if (startField == IntervalField.MONTH && end == IntervalField.MONTH) {
            return parsePeriodMonths(value, INTERVAL_MONTH_FORMATTER, startField, end);
        }

        throw new IllegalArgumentException("Invalid year month interval qualifier: " + startField + " to " + end);
    }

    private static long parsePeriodMonths(String value, PeriodFormatter periodFormatter, IntervalField startField, IntervalField endField)
    {
        try {
            Period period = parsePeriod(periodFormatter, value);
            return IntervalYearMonth.toMonths(
                    period.getValue(YEAR_FIELD),
                    period.getValue(MONTH_FIELD));
        }
        catch (IllegalArgumentException e) {
            throw invalidInterval(e, value, startField, endField);
        }
    }

    private static Period parsePeriod(PeriodFormatter periodFormatter, String value)
    {
        boolean negative = value.startsWith("-");

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Ensure the qualifier is YEAR, YEAR TO MONTH, or MONTH and the value matches (e.g. '2-6')
  2. Route DAY/HOUR/MINUTE/SECOND combinations to parseDayTimeInterval instead
  3. Validate the qualifier string against the allowed set before invoking the parser
  4. Fix reversed fields such as MONTH TO YEAR — Presto requires start <= end in the field hierarchy

Example fix

-- before
INTERVAL '1-2' MONTH TO YEAR
-- after
INTERVAL '1-2' YEAR TO MONTH
Defensive patterns

Strategy: validation

Validate before calling

boolean valid = (start == IntervalField.YEAR && (end == IntervalField.YEAR || end == IntervalField.MONTH)) || (start == IntervalField.MONTH && end == IntervalField.MONTH);

Try / catch

try { return parseYearMonthInterval(value, start, end); } catch (IllegalArgumentException e) { return parseDayTimeInterval(value, start, end); }

Prevention

When it happens

Trigger: Calling parseYearMonthInterval with day/time fields (e.g. startField=DAY or end=SECOND), which belong to the DAY–SECOND parser, or a nonsensical combination like MONTH TO YEAR.

Common situations: Routing mistakes where a DAY TO SECOND literal is sent to the year-month parser; user SQL like INTERVAL '1-2' DAY TO HOUR incorrectly typed; code building interval qualifiers dynamically from config without validation.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/3bbc058831be76cd. Report an issue: GitHub.