actualbudget/actual · error

validation-error

validation-error

Error message

err.message (rethrown ValidationError message)

What it means

If exportModel.validate() raises a ValidationError while parsing the imported dashboard (missing fields, wrong data types, unknown widget types), importDashboard rethrows the original message as `Error(err.message, { cause: 'validation-error' })`. The message is the specific validation failure (e.g. "Invalid widget.0.type value ...").

Source

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

                ]),
              ),
              tombstone: false,
            }),
          ),
      ]);
    });

    return { status: 'ok' as const };
  } catch (err: unknown) {
    if (err instanceof Error) {
      err.message = 'Error importing file: ' + err.message;
      captureException(err);
    }
    if (err instanceof SyntaxError) {
      throw new Error('Invalid JSON file.', { cause: 'json-parse-error' });
    }
    if (err instanceof ValidationError) {
      throw new Error(err.message, { cause: 'validation-error' });
    }
    throw new Error('Internal error occurred during import.', {
      cause: 'internal-error',
    });
  }
}

export type DashboardHandlers = {
  'dashboard-create': typeof createDashboardPage;
  'dashboard-delete': typeof deleteDashboardPage;
  'dashboard-rename': typeof renameDashboardPage;
  'dashboard-update': typeof updateDashboard;
  'dashboard-update-widget': typeof updateDashboardWidget;
  'dashboard-reset': typeof resetDashboard;
  'dashboard-add-widget': typeof addDashboardWidget;
  'dashboard-remove-widget': typeof removeDashboardWidget;
  'dashboard-copy-widget': typeof copyDashboardWidget;
  'dashboard-import': typeof importDashboard;

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Read the rethrown message — it names the exact field and index that failed; fix that field in the JSON.
  2. Compare the file against a freshly exported dashboard from the same Actual version and align the schema.
  3. Ensure required fields (version, widgets, per-widget type/x/y/width/height, meta for custom-report) are present and correctly typed.

Example fix

// before
{ "widgets": [{ "type": "net-worth-card", "x": "0", "y": 0, "width": 1, "height": 1 }] }
// after
{ "version": 1, "widgets": [{ "type": "net-worth-card", "x": 0, "y": 0, "width": 1, "height": 1 }] }
Defensive patterns

Strategy: validation

Validate before calling

const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
if (typeof data.version !== 'number' || !Array.isArray(data.widgets)) {
  throw new Error('Dashboard file must have numeric version and widgets array');
}
data.widgets.forEach((w, i) => {
  for (const f of ['type','x','y','width','height']) {
    if (w[f] === undefined) throw new Error(`widgets[${i}] missing ${f}`);
  }
  for (const f of ['x','y','width','height']) {
    if (!Number.isInteger(w[f])) throw new Error(`widgets[${i}].${f} must be an integer`);
  }
});

Type guard

function isDashboardExport(v: unknown): v is ExportImportDashboard {
  return (
    typeof v === 'object' && v !== null &&
    typeof (v as any).version === 'number' &&
    Array.isArray((v as any).widgets) &&
    (v as any).widgets.every((w: any) =>
      typeof w.type === 'string' &&
      Number.isInteger(w.x) && Number.isInteger(w.y) &&
      Number.isInteger(w.width) && Number.isInteger(w.height))
  );
}

Try / catch

try {
  await send('dashboard-import', { filePath, dashboardPageId });
} catch (e) {
  if (e.cause === 'validation-error') {
    console.error(`Schema problem in import file: ${e.message}`);
  }
}

Prevention

When it happens

Trigger: Importing a dashboard JSON whose structure violates the ExportImportDashboard schema: missing version/widgets, non-integer x/y/width/height, unknown widget type, missing meta on custom-report widgets, or invalid report meta.

Common situations: Schema drift between Actual versions; hand-edited export files; exports from third-party tooling that guesses the format.

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