{"record":{"id":"91fbbd5fabaef69b","repo":"grafana/grafana","slug":"elements-is-not-an-object-or-is-null","errorCode":null,"errorMessage":"Elements is not an object or is null","messagePattern":"Elements is not an object or is null","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts","lineNumber":854,"sourceCode":"\n// Function to know if the dashboard transformed is a valid DashboardV2Spec\nexport function validateDashboardSchemaV2(dash: unknown): dash is DashboardV2Spec {\n  if (typeof dash !== 'object' || dash === null || Array.isArray(dash)) {\n    throw new Error('Dashboard is not an object or is null');\n  }\n\n  // Required properties\n  if (!('title' in dash) || typeof dash.title !== 'string') {\n    throw new Error('Title is not a string');\n  }\n  if (!('timeSettings' in dash) || typeof dash.timeSettings !== 'object' || dash.timeSettings === null) {\n    throw new Error('TimeSettings is not an object or is null');\n  }\n  if (!('variables' in dash) || !Array.isArray(dash.variables)) {\n    throw new Error('Variables is not an array');\n  }\n  if (!('elements' in dash) || typeof dash.elements !== 'object' || dash.elements === null) {\n    throw new Error('Elements is not an object or is null');\n  }\n  if (!('annotations' in dash) || !Array.isArray(dash.annotations)) {\n    throw new Error('Annotations is not an array');\n  }\n  if (!('layout' in dash) || typeof dash.layout !== 'object' || dash.layout === null) {\n    throw new Error('Layout is not an object or is null');\n  }\n\n  // Optional properties - only validate if present\n  if ('description' in dash && dash.description !== undefined && typeof dash.description !== 'string') {\n    throw new Error('Description is not a string');\n  }\n  if ('cursorSync' in dash && dash.cursorSync !== undefined) {\n    const validCursorSyncValues = ((): string[] => {\n      const typeValues: DashboardCursorSync[] = ['Off', 'Crosshair', 'Tooltip'];\n      return typeValues;\n    })();\n","sourceCodeStart":836,"sourceCodeEnd":872,"githubUrl":"https://github.com/grafana/grafana/blob/ae3104e3690e28e72c8f0c97efc12647a70f75c3/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts#L836-L872","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure getElements always returns an object (default to {} for empty dashboards).","Type the return as Record<string, ElementSpec> so a regression to array is a compile error.","Unit-test the empty-dashboard transform path.","Check upstream element-id generation if elements went missing."],"exampleFix":"// before\nif (!('elements' in dash) || typeof dash.elements !== 'object' || dash.elements === null) {\n  throw new Error('Elements is not an object or is null');\n}\n\n// after — default in the producer\nelements: getElements(scene, dsReferencesMapping, isSnapshot) ?? {},","handlingStrategy":"type-guard","validationCode":"const els = getElements(scene, dsRefs, isSnapshot);\nif (typeof els !== 'object' || els === null) throw new Error('getElements must return an object');","typeGuard":"function isElementsObject(dash: unknown): dash is { elements: Record<string, unknown> } {\n  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;\n}","tryCatchPattern":"try { await transformSceneToSaveModelSchemaV2(scene); }\ncatch (e) {\n  if (e.message === 'Elements is not an object or is null') reportBug('getElements returned non-object');\n  else throw e;\n}","preventionTips":["Type getElements' return as Record<string, ElementSpec>.","Default to {} for empty dashboards.","Add a unit test for the empty-dashboard path."],"tags":["validation","v2","elements","required-field"],"backgroundTag":null,"analyzedSha":"ae3104e3690e28e72c8f0c97efc12647a70f75c3","analyzedAt":"2026-08-12T12:48:38.752Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}