{"record":{"id":"eacbbc848dd0caa4","repo":"grafana/grafana","slug":"cannot-add-addingtype-at-parentpath-maxim","errorCode":null,"errorMessage":"Cannot add ${addingType} at \"${parentPath}\": maximum nesting depth (${MAX_NESTING_DEPTH} group layers) would be exceeded.","messagePattern":"Cannot add (.+?) at \"(.+?)\": maximum nesting depth \\((.+?) group layers\\) would be exceeded\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"public/app/features/dashboard-scene/mutation-api/commands/layoutPathResolver.ts","lineNumber":195,"sourceCode":"\n  const isAlreadyTargetType =\n    (addingType === 'rows' && targetLayout instanceof RowsLayoutManager) ||\n    (addingType === 'tabs' && targetLayout instanceof TabsLayoutManager);\n\n  // Appending an item to an existing group of the same type doesn't add a nesting layer\n  if (isAlreadyTargetType) {\n    return;\n  }\n\n  // The new group becomes the direct layout of the last segment's item\n  if (addingType === 'tabs' && segments.length > 0 && segments[segments.length - 1].type === 'tabs') {\n    throw new Error(`Cannot add tabs at \"${parentPath}\": tabs cannot be nested directly inside tabs.`);\n  }\n\n  // Wrapping the target layout in a new group adds one layer on top of everything nested inside it\n  const resultingDepth = segments.length + 1 + getGroupDepth(targetLayout);\n  if (resultingDepth > MAX_NESTING_DEPTH) {\n    throw new Error(\n      `Cannot add ${addingType} at \"${parentPath}\": maximum nesting depth (${MAX_NESTING_DEPTH} group layers) would be exceeded.`\n    );\n  }\n}\n\n/**\n * Resolve a path up to the parent level (one segment before the last).\n * Returns the parent layout manager and the last segment info.\n * Useful for operations that need to manipulate the parent (e.g., remove/insert).\n */\nexport function resolveParentPath(\n  body: DashboardLayoutManager,\n  path: string\n): { parent: DashboardLayoutManager; segment: PathSegment } {\n  const segments = parsePathSegments(path);\n\n  if (segments.length === 0) {\n    throw new Error(`Cannot resolve parent of root path \"/\"`);","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/grafana/grafana/blob/ae3104e3690e28e72c8f0c97efc12647a70f75c3/public/app/features/dashboard-scene/mutation-api/commands/layoutPathResolver.ts#L177-L213","documentation":"Thrown by validateNesting when wrapping the target layout in a new group (rows or tabs) would push total group nesting beyond MAX_NESTING_DEPTH (4). The check is segments.length + 1 + getGroupDepth(targetLayout), counting both ancestor segments and the wrapped subtree's own depth.","triggerScenarios":"ADD_ROW or ADD_TAB at a parentPath deep enough that adding one more group layer, plus the depth of the layout being wrapped, exceeds 4. Example: wrapping a rows>tabs>rows subtree (depth 3) at path depth 2 → 2+1+3 = 6 > 4.","commonSituations":"Aggressive grouping of already-deep layouts; automation that wraps content repeatedly; migration that introduces extra nesting layers on top of an already-nested dashboard.","solutions":["Reduce existing nesting before adding another group layer (flatten or remove a level).","Add to an existing group of the same type at the target (the isAlreadyTargetType branch doesn't add a layer).","Move panels into a shallower existing group instead of creating a new nested group.","Re-read GET_LAYOUT to compute current depth before attempting the add."],"exampleFix":"// before - would exceed MAX_NESTING_DEPTH (4)\nawait mutate('ADD_ROW', { row, parentPath: '/tabs/0/rows/0' }); // wrapping deep subtree\n\n// after - add to an existing rows layout (same-type, no new layer)\nawait mutate('ADD_ROW', { row, parentPath: '/tabs/0/rows/0' }); // only if target IS already RowsLayoutManager","handlingStrategy":"validation","validationCode":"// Compute the resulting depth before calling ADD_ROW/ADD_TAB.\nimport { getGroupDepth, MAX_NESTING_DEPTH } from '../../scene/layouts-shared/nestingRestrictions';\nimport { resolveLayoutPath, parsePathSegmentsLike } from './layoutPathResolver';\n\nconst { layoutManager } = resolveLayoutPath(scene.state.body, parentPath);\nconst segLen = parentPath === '/' ? 0 : parentPath.slice(1).split('/').length / 2;\nconst alreadySameType =\n  (addingType === 'rows' && layoutManager instanceof RowsLayoutManager) ||\n  (addingType === 'tabs' && layoutManager instanceof TabsLayoutManager);\nif (!alreadySameType && segLen + 1 + getGroupDepth(layoutManager) > MAX_NESTING_DEPTH) {\n  return { skip: true, reason: 'would exceed MAX_NESTING_DEPTH' };\n}","typeGuard":"import { MAX_NESTING_DEPTH, getGroupDepth } from '../../scene/layouts-shared/nestingRestrictions';\nfunction wouldExceedMaxDepth(segments: number, target: DashboardLayoutManager): boolean {\n  return segments + 1 + getGroupDepth(target) > MAX_NESTING_DEPTH;\n}","tryCatchPattern":"const res = await mutate(cmd, payload);\nif (!res.success && /maximum nesting depth/.test(res.error)) {\n  // flatten one level (remove a group) or add to an existing same-type group, then retry\n}","preventionTips":["Keep dashboard layouts shallow; MAX_NESTING_DEPTH is 4 group layers.","Adding to an existing same-type group does not consume a new layer — prefer that.","Compute resulting depth before wrapping; flatten or move panels into shallower groups instead."],"tags":["layout","validation","dashboard-scene","mutation-api","nesting","depth-limit"],"backgroundTag":null,"analyzedSha":"ae3104e3690e28e72c8f0c97efc12647a70f75c3","analyzedAt":"2026-08-12T12:48:38.752Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}