Skip to content

Validation

Pre-mount checks for chartData. For the look-and-export loop, see Visual QA.

ChartBuddy validates chart data at runtime, not just at compile time. TypeScript types are erased at build time, and chart data almost always arrives at runtime — parsed JSON, a Sheets range, untyped JSON, or an untyped setChartData().

Two ways in:

  • new Insight(), setChartData(), setData(), and update() validate their input and throw ChartDataValidationError on an error-severity problem.
  • validateChartData() never throws and returns every problem it found. Use it to check a config before you mount one.

The second is what makes a generate → check → repair loop possible without rendering anything, which matters when configs are generated or assembled at runtime.

Checking before you mount

js
import { validateChartData, Insight } from '@chartbuddy.io/embed';

const { valid, errors, warnings } = validateChartData(candidate);

if (!valid) {
  // Branch on `code`, fix the value at `path`.
  for (const issue of errors) {
    console.log(issue.code, issue.path, issue.expected, issue.received);
  }
} else {
  new Insight('#chart', { chartData: candidate });
}

ValidationResult

FieldTypeMeaning
validbooleanTrue when there are no error-severity issues. Warnings do not invalidate.
issuesValidationIssue[]Everything found, errors and warnings, in document order.
errorsValidationIssue[]The error-severity subset — what makes valid false.
warningsValidationIssue[]Rendered anyway, but probably a bug.

ValidationIssue

FieldTypeAlways presentMeaning
pathstringyesWhere the problem is, e.g. pie.innerRadiusRatio, seriesData[2][0]. Empty string for whole-object problems.
codeValidationIssueCodeyesStable classification. Branch on this.
severity'error' | 'warning'yesErrors reject at the API boundary; warnings are logged.
messagestringyesFor humans. May be reworded in any release — never parse it.
expectedstringnoWhat the schema wanted: 'number', 'integer', '>= 0', '2D array'.
receivedstringnoWhat arrived: 'string', 'null', '1.5'.
allowedstring[]noThe full legal set, for not-in-enum and unknown-chart-type.
suggestionstringnoNearest legal value, when one is close enough to be a likely typo.

Optional fields are omitted, not set to undefined, so issues serialize cleanly to JSON.

Issue codes

code is the stable contract. New codes may be added in a minor release, so treat an unrecognized code as a generic failure rather than crashing.

CodeSeverityCauseRepair
not-an-objecterrorThe value is not a chart-data object at all.Pass an object.
unknown-chart-typeerrorchartType is not a known type or alias.Use suggestion, or pick from allowed.
empty-patcherrorNeither chartType nor seriesData supplied.Include at least one, or set requireSomething: false.
wrong-typeerrorA known field holds the wrong JavaScript type.Coerce to expected. Covers NaN / Infinity.
out-of-rangeerrorA numeric field is outside its documented range.Clamp to the bound in expected.
not-in-enumerrorA string field is outside its legal set.Use suggestion, or pick from allowed.
series-data-shapeerrorseriesData is not a usable grid for this chart type.Reshape — expected names the row/column minimum.
ragged-series-datawarningRows have unequal lengths.Pad the short rows. The renderer pads for you, but the result is rarely what you meant.
foreign-option-bagerrorAn option bag belongs to a different chart type.Move the options to the bag in suggestion.

Unknown keys are never an issue at any level. Extra fields are ignored.

Repairing automatically

suggestion and allowed exist so a fix does not need a second model call:

js
function repair(chartData) {
  const patched = structuredClone(chartData);
  for (const issue of validateChartData(patched).errors) {
    if (issue.suggestion) setAtPath(patched, issue.path, issue.suggestion);
  }
  return patched;
}

'clusterdBar'suggestion: 'clusteredBar'; orientation: 'verical'suggestion: 'vertical'.

Catching the throw

When you skip the pre-check, the API boundary still stops bad data. The thrown error carries the same structured issues:

js
import { ChartDataValidationError } from '@chartbuddy.io/embed';

try {
  insight.setChartData(candidate);
} catch (err) {
  if (err instanceof ChartDataValidationError) {
    console.log(err.errors);   // error-severity issues
    console.log(err.warnings); // logged, did not cause the throw
    console.log(err.issues);   // both
    JSON.stringify(err);       // { name, message, issues }
  }
}

Warnings never throw. They go to console.warn and the call proceeds.

Options

js
validateChartData(chartData, {
  form: 'auto',            // 'patch' | 'resolved' | 'auto'
  requireSomething: true,  // demand chartType and/or seriesData
  maxIssues: 20,           // cap the report
});

form decides whether a foreign option bag is a mistake. A hand-authored patch names one chart type, so { chartType: 'pie', bar: {…} } is worth reporting. A resolved snapshot from getChartData() legitimately carries every bag, because defaults seed them all. auto tells them apart by whether every bag is present, which is right in practice — pass form explicitly when you know.

requireSomething should be false when validating a styling-only patch:

js
validateChartData({ title: { text: 'FY26' } }, { requireSomething: false });

Formatting for humans

js
import { formatValidationIssues } from '@chartbuddy.io/embed';

console.error(formatValidationIssues(result.issues, '[my-app]'));

Where else validation runs

Inside the ChartBuddy app, validation is advisory (logged, not thrown) so charts keep opening while you edit. The embed API is strict and throws, so callers can catch and fix bad configs before retrying.

Generating valid data in the first place

The machine-readable schema ships in the package and describes every field the validator checks:

js
import schema from '@chartbuddy.io/embed/chart-schema.json';

Constrain generation with it and most of this page stops mattering. See chartData schema.

Developer documentation for ChartBuddy Embed · Not the end-user Help Center · Help Center