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

  1. Log dash.cursorSync at the call site to see the actual offending value.
  2. Map the source value through transformCursorSynctoEnum (or the DashboardCursorSync type) so only 'Off'|'Crosshair'|'Tooltip' are emitted; coerce unknown values to the default ('Off').
  3. If consuming external JSON, normalize cursorSync with a lookup table before passing the object to the validator.
  4. 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

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


AI-assisted analysis of grafana/grafana@ae3104e369 (2026-08-12). Data as JSON: /api/errors/d57edeab8089ead6. Report an issue: GitHub.