actualbudget/actual · error · ValidationError

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

Error message

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

What it means

Each widget's x (grid column position) must be an integer. The dashboard import validator throws this ValidationError when widget.x is a float, string, null, or undefined, because grid coordinates must be whole numbers for the layout engine.

Source

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

    if (!Array.isArray(dashboard.widgets)) {
      throw new ValidationError(
        'Invalid dashboard.widgets data type: it must be an array of widgets.',
      );
    }

    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(

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Round or floor the x value: widget.x = Math.round(widget.x).
  2. Ensure every widget object includes a numeric integer x before import.
  3. Convert string numbers: widget.x = Number(widget.x) then check Number.isInteger.
  4. Fix the generating script so grid positions come from Math.floor of computed values.
  5. Re-export from the app to get properly typed coordinates.

Example fix

// before
widgets.push({ type: 'net', x: pos * 1.5, y: 0, width: 1, height: 1 });
// after
widgets.push({ type: 'net', x: Math.round(pos * 1.5), y: 0, width: 1, height: 1 });
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

function hasIntegerCoord(w: { x: unknown }): w is { x: number } {
  return Number.isInteger(w.x);
}

Try / catch

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

Prevention

When it happens

Trigger: Importing dashboard JSON where a widget's x is e.g. 1.5, "0", null, or the field is absent; hand-editing or programmatically generating the export with non-integer coordinates; parsing coordinates from float math elsewhere.

Common situations: Layouts computed from percentage math producing fractional x values; JSON round-trips through tools that stringify numbers; hand-crafted widget definitions missing x entirely.

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/c7de6f67b055188a. Report an issue: GitHub.