actualbudget/actual · error · CompileError

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

Error message

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

What it means

castInput needs a `date-month` value (YYYY-MM). Accepted sources are `date`, `string`, and `any`; anything else (integer, boolean, id, array, etc.) cannot be cast and throws. Unlike `date`, non-literal string fields ARE allowed here via SQL SUBSTR, so the failure is limited to genuinely uncastable types.

Source

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

      } else {
        throw new CompileError(
          'Casting string fields to dates is not supported',
        );
      }
    }

    throw new CompileError(`Can't cast ${expr.type} to date`);
  } else if (type === 'date-month') {
    let expr2;
    if (expr.type === 'date') {
      expr2 = expr;
    } else if (expr.type === 'string' || expr.type === 'any') {
      expr2 =
        parseMonth(expr.value) ||
        parseDate(expr.value) ||
        badDateFormat(expr.value, 'date-month');
    } else {
      throw new CompileError(`Can't cast ${expr.type} to date-month`);
    }

    if (expr2.literal) {
      return typed(
        dateToInt(expr2.value.toString().slice(0, 6)),
        'date-month',
        { literal: true },
      );
    } 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;

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Pass a date-typed field or a string like '2024-01' or '2024-01-15' instead
  2. Verify the field name refers to a date column, not a numeric/id column
  3. Convert the value to a month string ('YYYY-MM') before querying
  4. Drop the invalid expression and rebuild the month filter around the `date` field

Example fix

// before
q.groupBy({ $month: { $field: 'account' } })
// after
q.groupBy({ $month: { $field: 'date' } })
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

try {
  runQuery(q.groupBy({ $month: { $field: field } }));
} catch (e) {
  if (/Can't cast .* to date-month/.test(e.message)) {
    throw new Error(`Field '${field}' is not a date; use the date column`, { cause: e });
  }
  throw e;
}

Prevention

When it happens

Trigger: Using a date-month function (e.g. month grouping/filters) with an expression typed as something other than date/string/any/param/null — commonly an integer, float, boolean, id, or array value.

Common situations: Grouping by month but passing an account id or numeric field instead of `date`; passing a rule value of the wrong type through the API; a schema mismatch after renaming fields.

Related errors


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