actualbudget/actual · error · ValidationError

Invalid widget.${idx}.y data-type for value ${widget.y}.

Error message

Invalid widget.${idx}.y data-type for value ${widget.y}.

What it means

Each widget's y (grid row position) must be an integer. The dashboard import validator throws this ValidationError when widget.y is fractional, a string, null, or missing, since the grid layout requires whole-number rows.

Source

Thrown at packages/loot-core/src/server/dashboard/app.ts:79

    dashboard.widgets.forEach((widget, idx) => {
      requiredFields(`Dashboard widget #${idx}`, widget, [
        'type',
        'x',
        'y',
        'width',
        'height',
        ...(isExportedCustomReportWidget(widget) ? ['meta' as const] : []),
      ]);

      if (!Number.isInteger(widget.x)) {
        throw new ValidationError(
          `Invalid widget.${idx}.x data-type for value ${widget.x}.`,
        );
      }

      if (!Number.isInteger(widget.y)) {
        throw new ValidationError(
          `Invalid widget.${idx}.y data-type for value ${widget.y}.`,
        );
      }

      if (!Number.isInteger(widget.width)) {
        throw new ValidationError(
          `Invalid widget.${idx}.width data-type for value ${widget.width}.`,
        );
      }

      if (!Number.isInteger(widget.height)) {
        throw new ValidationError(
          `Invalid widget.${idx}.height data-type for value ${widget.height}.`,
        );
      }

      if (!isWidgetType(widget.type)) {
        throw new ValidationError(

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Set widget.y = Math.round(widget.y) before import.
  2. Ensure every widget has an explicit integer y (e.g. sequential row indices).
  3. Coerce strings with Number() and verify with Number.isInteger.
  4. Fix the generator to compute rows with integer counters, not division results.
  5. Re-export from the app for a valid baseline.

Example fix

// before
let y = 0; for (const w of defs) { w.y = y; y += w.height / 2; }
// after
let y = 0; for (const w of defs) { w.y = y; y += Math.ceil(w.height); }
Defensive patterns

Strategy: validation

Validate before calling

widgets.forEach((w, i) => {
  if (!Number.isInteger(w.y)) throw new Error(`Widget #${i}: y must be an integer, got ${w.y}`);
});

Type guard

function hasIntegerRow(w: { y: unknown }): w is { y: number } {
  return Number.isInteger(w.y);
}

Try / catch

try {
  await send('dashboard-import', { version: 1, widgets });
} catch (e) {
  if (e instanceof ValidationError && e.message.includes('.y data-type')) {
    console.error('Non-integer y coordinate in import; round rows and retry');
  } else throw e;
}

Prevention

When it happens

Trigger: Importing dashboard JSON where a widget's y is 2.5, "1", null, or undefined; generated exports using float row math or omitting y entirely.

Common situations: Scripted dashboard generation stacking widgets with computed float offsets; JSON edited in a spreadsheet that emits string numerics; exports from tooling that forgot the y field.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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