Appearance
Validation
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, a model's tool call, or an untyped setChartData().
Two ways in:
new Insight(),setChartData(),setData(), andupdate()validate their input and throwChartDataValidationErroron 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 most when a model authored the config.
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
| Field | Type | Meaning |
|---|---|---|
valid | boolean | True when there are no error-severity issues. Warnings do not invalidate. |
issues | ValidationIssue[] | Everything found, errors and warnings, in document order. |
errors | ValidationIssue[] | The error-severity subset — what makes valid false. |
warnings | ValidationIssue[] | Rendered anyway, but probably a bug. |
ValidationIssue
| Field | Type | Always present | Meaning |
|---|---|---|---|
path | string | yes | Where the problem is, e.g. pie.innerRadiusRatio, seriesData[2][0]. Empty string for whole-object problems. |
code | ValidationIssueCode | yes | Stable classification. Branch on this. |
severity | 'error' | 'warning' | yes | Errors reject at the API boundary; warnings are logged. |
message | string | yes | For humans. May be reworded in any release — never parse it. |
expected | string | no | What the schema wanted: 'number', 'integer', '>= 0', '2D array'. |
received | string | no | What arrived: 'string', 'null', '1.5'. |
allowed | string[] | no | The full legal set, for not-in-enum and unknown-chart-type. |
suggestion | string | no | Nearest 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.
| Code | Severity | Cause | Repair |
|---|---|---|---|
not-an-object | error | The value is not a chart-data object at all. | Pass an object. |
unknown-chart-type | error | chartType is not a known type or alias. | Use suggestion, or pick from allowed. |
empty-patch | error | Neither chartType nor seriesData supplied. | Include at least one, or set requireSomething: false. |
wrong-type | error | A known field holds the wrong JavaScript type. | Coerce to expected. Covers NaN / Infinity. |
out-of-range | error | A numeric field is outside its documented range. | Clamp to the bound in expected. |
not-in-enum | error | A string field is outside its legal set. | Use suggestion, or pick from allowed. |
series-data-shape | error | seriesData is not a usable grid for this chart type. | Reshape — expected names the row/column minimum. |
ragged-series-data | warning | Rows have unequal lengths. | Pad the short rows. The renderer pads for you, but the result is rarely what you meant. |
foreign-option-bag | error | An 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. Configs legitimately carry keys a given build predates; shape changes are handled by migrations.
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
Charts saved by older builds are migrated first and validated after, and there the result is only logged — never thrown — so an old chart always still opens. The strict, throwing behaviour is reserved for the embed API, which has no backward-compatibility burden and where a descriptive error lets an authoring agent correct itself in one turn.
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.