grafana/grafana · error · Error
CursorSync is not a valid value
Error message
CursorSync is not a valid value
What it means
Thrown by validateDashboardSchemaV2 (transformSceneToSaveModelSchemaV2.ts:874) when dash.cursorSync is present but not one of the three DashboardCursorSync enum literals 'Off', 'Crosshair', 'Tooltip'. The guard exists because the V2 save model (CUE schema in kinds/) constrains cursorSync to that union; an out-of-enum value would fail server-side CUE validation on save. transformSceneToSaveModelSchemaV2 wraps this throw and rethrows as 'Error transforming dashboard to schema v2: ...', so the save aborts.
Source
Thrown at public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts:874
if (!('annotations' in dash) || !Array.isArray(dash.annotations)) {
throw new Error('Annotations is not an array');
}
if (!('layout' in dash) || typeof dash.layout !== 'object' || dash.layout === null) {
throw new Error('Layout is not an object or is null');
}
// Optional properties - only validate if present
if ('description' in dash && dash.description !== undefined && typeof dash.description !== 'string') {
throw new Error('Description is not a string');
}
if ('cursorSync' in dash && dash.cursorSync !== undefined) {
const validCursorSyncValues = ((): string[] => {
const typeValues: DashboardCursorSync[] = ['Off', 'Crosshair', 'Tooltip'];
return typeValues;
})();
if (typeof dash.cursorSync !== 'string' || !validCursorSyncValues.includes(dash.cursorSync)) {
throw new Error('CursorSync is not a valid value');
}
}
if ('liveNow' in dash && dash.liveNow !== undefined && typeof dash.liveNow !== 'boolean') {
throw new Error('LiveNow is not a boolean');
}
if ('preload' in dash && dash.preload !== undefined && typeof dash.preload !== 'boolean') {
throw new Error('Preload is not a boolean');
}
if ('editable' in dash && dash.editable !== undefined && typeof dash.editable !== 'boolean') {
throw new Error('Editable is not a boolean');
}
if ('links' in dash && dash.links !== undefined && !Array.isArray(dash.links)) {
throw new Error('Links is not an array');
}
if ('tags' in dash && dash.tags !== undefined && !Array.isArray(dash.tags)) {
throw new Error('Tags is not an array');
}
if ('id' in dash && dash.id !== undefined && typeof dash.id !== 'number') {View on GitHub (pinned to ae3104e369)
Solutions
- Log dash.cursorSync at the call site to see the actual offending value.
- Map the source value through transformCursorSynctoEnum (or the DashboardCursorSync type) so only 'Off'|'Crosshair'|'Tooltip' are emitted; coerce unknown values to the default ('Off').
- If consuming external JSON, normalize cursorSync with a lookup table before passing the object to the validator.
- Type the producer field as DashboardCursorSync so TypeScript rejects invalid literals at compile time.
Example fix
// before
const dash = { ...spec, cursorSync: source.sync }; // source.sync could be undefined/'Crosshair '
// after
import type { DashboardCursorSync } from '@grafana/schema';
const CURSOR_SYNC: DashboardCursorSync[] = ['Off', 'Crosshair', 'Tooltip'];
const dash = {
...spec,
cursorSync: CURSOR_SYNC.includes(source.sync) ? source.sync : 'Off',
}; Defensive patterns
Strategy: type-guard
Validate before calling
const CURSOR_SYNC = ['Off', 'Crosshair', 'Tooltip'] as const;
const v = (dash as any)?.cursorSync;
if (v != null && !(CURSOR_SYNC as readonly string[]).includes(v)) {
throw new Error(`cursorSync '${v}' is invalid`);
} Type guard
import type { DashboardCursorSync } from '@grafana/schema';
const VALUES = ['Off', 'Crosshair', 'Tooltip'] as readonly DashboardCursorSync[];
function isValidCursorSync(v: unknown): v is DashboardCursorSync {
return typeof v === 'string' && (VALUES as readonly string[]).includes(v);
} Try / catch
try {
validateDashboardSchemaV2(dash);
} catch (e) {
if (String(e).includes('CursorSync')) { dash.cursorSync = 'Off'; /* retry with default */ }
else throw e;
} Prevention
- Type cursorSync as DashboardCursorSync at every producer so invalid literals are compile errors.
- Route all cursor-sync values through transformCursorSynctoEnum before building the spec.
- Normalize imported JSON: lowercase/trim, then map to the enum or default to 'Off'.
When it happens
Trigger: Calling transformSceneToSaveModelSchemaV2(scene) when the scene's CursorSync behavior state.sync flows through transformCursorSynctoEnum and yields a value outside the enum (e.g. undefined leaked through, or a numeric sync mode); OR calling validateDashboardSchemaV2(dash) directly on hand-built/parsed JSON where dash.cursorSync is a string like 'crosshair' (lowercase) or 'None'.
Common situations: Importing a V2 dashboard JSON whose cursorSync used different casing or an older enum label; external tools/provisioning systems that emit cursorSync as a free-form string; a refactor of CursorSync sync modes that transformCursorSynctoEnum did not cover.
Related errors
- WeekStart should be one of "saturday", "sunday" or "monday"
- JSON needs a fields definition
- Error transforming dashboard to schema v2: {reason}
- LiveNow is not a boolean
- Preload is not a boolean
AI-assisted analysis of grafana/grafana@ae3104e369 (2026-08-12).
Data as JSON: /api/errors/d57edeab8089ead6.
Report an issue: GitHub.