grafana/grafana · error · Error

Elements is not an object or is null

Error message

Elements is not an object or is null

What it means

Required-field check: elements must be a non-null object. In V2, elements is a map keyed by element id (panels, rows, etc.), not an array. Thrown when getElements returned undefined/null or the transformed dashboard lacks the elements key.

Source

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

// 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'];
      return typeValues;
    })();

View on GitHub (pinned to ae3104e369)

Solutions

  1. Ensure getElements always returns an object (default to {} for empty dashboards).
  2. Type the return as Record<string, ElementSpec> so a regression to array is a compile error.
  3. Unit-test the empty-dashboard transform path.
  4. Check upstream element-id generation if elements went missing.

Example fix

// before
if (!('elements' in dash) || typeof dash.elements !== 'object' || dash.elements === null) {
  throw new Error('Elements is not an object or is null');
}

// after — default in the producer
elements: getElements(scene, dsReferencesMapping, isSnapshot) ?? {},
Defensive patterns

Strategy: type-guard

Validate before calling

const els = getElements(scene, dsRefs, isSnapshot);
if (typeof els !== 'object' || els === null) throw new Error('getElements must return an object');

Type guard

function isElementsObject(dash: unknown): dash is { elements: Record<string, unknown> } {
  return typeof dash === 'object' && dash !== null && 'elements' in dash && typeof (dash as any).elements === 'object' && !Array.isArray((dash as any).elements) && (dash as any).elements !== null;
}

Try / catch

try { await transformSceneToSaveModelSchemaV2(scene); }
catch (e) {
  if (e.message === 'Elements is not an object or is null') reportBug('getElements returned non-object');
  else throw e;
}

Prevention

When it happens

Trigger: getElements(scene, dsReferencesMapping, isSnapshot) returned undefined; or the scene had no panels and the transformer did not emit an empty object. Also when a future refactor returns an array by mistake.

Common situations: Empty dashboard with no panels where the transformer skipped emitting elements; refactor of getElements that changed its return type; element id collision causing a throw upstream that was caught and returned undefined.

Related errors


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