actualbudget/actual · error · CompileError

Can't cast ${expr.type} to date-year

Error message

Can't cast ${expr.type} to date-year

What it means

castInput needs a `date-year` value (YYYY). Accepted sources are `date`, `date-month`, `string`, `any`, params, and null; other types throw with the source type named. Strings are parsed via parseYear/parseMonth/parseDate, and date fields are truncated with SQL SUBSTR.

Source

Thrown at packages/loot-core/src/server/aql/compiler.ts:312

      );
    } else {
      return typed(
        `CAST(SUBSTR(${expr2.value}, 1, 6) AS integer)`,
        'date-month',
      );
    }
  } else if (type === 'date-year') {
    let expr2;
    if (expr.type === 'date' || expr.type === 'date-month') {
      expr2 = expr;
    } else if (expr.type === 'string') {
      expr2 =
        parseYear(expr.value) ||
        parseMonth(expr.value) ||
        parseDate(expr.value) ||
        badDateFormat(expr.value, 'date-year');
    } else {
      throw new CompileError(`Can't cast ${expr.type} to date-year`);
    }

    if (expr2.literal) {
      return typed(dateToInt(expr2.value.toString().slice(0, 4)), 'date-year', {
        literal: true,
      });
    } else {
      return typed(
        `CAST(SUBSTR(${expr2.value}, 1, 4) AS integer)`,
        'date-year',
      );
    }
  } else if (type === 'id') {
    if (expr.type === 'string') {
      return typed(expr.value, 'id', { literal: expr.literal });
    }
  } else if (type === 'float') {
    if (expr.type === 'integer') {

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Pass a date field or a string like '2024' / '2024-01' / '2024-01-15'
  2. Confirm the referenced field is the `date` column
  3. Convert the year to a string before passing it into the query
  4. Rebuild the filter using { $year: { $field: 'date' } }

Example fix

// before
q.filter({ $year: { $field: 'amount' } })
// after
q.filter({ $year: { $field: 'date' } })
Defensive patterns

Strategy: validation

Validate before calling

const YEAR_RE = /^(\d{4}(-\d{2}(-\d{2})?)?)$/;
if (!(typeof v === 'string' && YEAR_RE.test(v))) {
  throw new Error('Expected YYYY (or YYYY-MM-DD) string');
}

Type guard

function isYearLike(v) {
  return typeof v === 'string' && /^\d{4}(-\d{2}(-\d{2})?)?$/.test(v);
}

Try / catch

try {
  runQuery(q.filter({ $year: { $field: field } }));
} catch (e) {
  if (/Can't cast .* to date-year/.test(e.message)) {
    throw new Error(`Field '${field}' cannot be cast to a year`, { cause: e });
  }
  throw e;
}

Prevention

When it happens

Trigger: Using a year function/filter with an expression typed integer, float, boolean, id, array, etc. — anything that is not date/date-month/string/any.

Common situations: Yearly summary queries that accidentally reference a numeric column (amount, balance) instead of `date`; programmatic query builders that pass numbers for years into the wrong slot.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/2c3f81e86252298c. Report an issue: GitHub.