grafana/grafana · error · Error

Variables is not an array

Error message

Variables is not an array

What it means

Required-field check: variables must be an array. V2 stores variables as a list (each entry is a typed variable spec), unlike V1's object map. Thrown when the transformed dashboard has no variables key or a non-array value.

Source

Thrown at public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts:851

  return spec;
}

// Function to know if the dashboard transformed is a valid DashboardV2Spec
export function validateDashboardSchemaV2(dash: unknown): dash is DashboardV2Spec {
  if (typeof dash !== 'object' || dash === null || Array.isArray(dash)) {
    throw new Error('Dashboard is not an object or is null');
  }

  // Required properties
  if (!('title' in dash) || typeof dash.title !== 'string') {
    throw new Error('Title is not a string');
  }
  if (!('timeSettings' in dash) || typeof dash.timeSettings !== 'object' || dash.timeSettings === null) {
    throw new Error('TimeSettings is not an object or is null');
  }
  if (!('variables' in dash) || !Array.isArray(dash.variables)) {
    throw new Error('Variables is not an array');
  }
  if (!('elements' in dash) || typeof dash.elements !== 'object' || dash.elements === null) {
    throw new Error('Elements is not an object or is null');
  }
  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'];

View on GitHub (pinned to ae3104e369)

Solutions

  1. Ensure getVariables always returns an array (default to [] when there are no variables).
  2. If migrating from V1, convert the variables object map to the V2 array form before transform.
  3. Add a unit test for the no-variables case.
  4. Type the return of getVariables as DashboardV2Variable[] so the compiler catches regressions.

Example fix

// before
if (!('variables' in dash) || !Array.isArray(dash.variables)) {
  throw new Error('Variables is not an array');
}

// after — guarantee array in the producer
variables: getVariables(sceneDash, dsReferencesMapping) ?? [],
Defensive patterns

Strategy: type-guard

Validate before calling

const vars = getVariables(sceneDash, dsRefs);
if (!Array.isArray(vars)) throw new Error('getVariables must return an array');

Type guard

function isVariablesArray(dash: unknown): dash is { variables: unknown[] } {
  return typeof dash === 'object' && dash !== null && 'variables' in dash && Array.isArray((dash as any).variables);
}

Try / catch

try { await transformSceneToSaveModelSchemaV2(scene); }
catch (e) {
  if (e.message === 'Variables is not an array') reportBug('getVariables returned non-array');
  else throw e;
}

Prevention

When it happens

Trigger: getVariables(sceneDash, dsReferencesMapping) returned undefined or an object instead of an array. Happens when the scene has no variables layer or the variables serializer changed shape.

Common situations: Dashboard without any variables (the transformer should still emit []); migration code that returned the V1 object map; refactor of getVariables that dropped the array cast; template variable plugin returning a non-array.

Related errors


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