{"record":{"id":"3d8949cf571fd46f","repo":"grafana/grafana","slug":"cannot-convert-to-tabslayout-a-child-s-inner-layo","errorCode":null,"errorMessage":"Cannot convert to TabsLayout: a child's inner layout is already tabs, which would put tabs directly inside tabs.","messagePattern":"Cannot convert to TabsLayout: a child's inner layout is already tabs, which would put tabs directly inside tabs\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"public/app/features/dashboard-scene/mutation-api/commands/updateLayout.ts","lineNumber":109,"sourceCode":"  return [];\n}\n\nfunction validateGroupNesting(path: string, layoutType: LayoutType, currentLayout: DashboardLayoutManager): void {\n  // Rows can be nested inside both rows and tabs, and a group->group\n  // conversion doesn't change the nesting depth, so only tabs conversions\n  // need validation: tabs cannot end up directly inside tabs.\n  if (layoutType !== 'TabsLayout') {\n    return;\n  }\n\n  const pathParts = path === '/' ? [] : path.slice(1).split('/');\n  const parentSegmentType = pathParts.length >= 2 ? pathParts[pathParts.length - 2] : undefined;\n  if (parentSegmentType === 'tabs') {\n    throw new Error(`Cannot convert to TabsLayout at \"${path}\": tabs cannot be nested directly inside tabs.`);\n  }\n\n  if (hasDirectTabsChild(currentLayout)) {\n    throw new Error(\n      `Cannot convert to TabsLayout: a child's inner layout is already tabs, which would put tabs directly inside tabs.`\n    );\n  }\n}\n\nfunction switchLayout(\n  resolved: ReturnType<typeof resolveLayoutPath>,\n  newLayout: DashboardLayoutManager,\n  path: string\n): void {\n  if (resolved.item) {\n    if (!isLayoutParent(resolved.item)) {\n      throw new Error(`Cannot switch layout: item at \"${path}\" is not a LayoutParent`);\n    }\n    resolved.item.switchLayout(newLayout);\n  } else {\n    const layoutParent = resolved.layoutManager.parent;\n    if (!layoutParent || !isLayoutParent(layoutParent)) {","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/grafana/grafana/blob/ae3104e3690e28e72c8f0c97efc12647a70f75c3/public/app/features/dashboard-scene/mutation-api/commands/updateLayout.ts#L91-L127","documentation":"Thrown by validateGroupNesting() when converting the current layout to TabsLayout while hasDirectTabsChild() reports that one of its existing children already has an inner tabs layout. Converting would place tabs directly inside tabs because the child's inner tabs would become a direct descendant of the new tabs container.","triggerScenarios":"UPDATE_LAYOUT converts a layout to TabsLayout; the layout currently contains a child (row/tab) whose own inner layout is already TabsLayout, detected by hasDirectTabsChild (updateLayout.ts:108-112).","commonSituations":"Flattening/restructuring an existing layout that already contains nested tabs; an LLM reorganizer that converts a rows layout (containing a tabbed child) into tabs.","solutions":["First convert the inner tabs child to RowsLayout (or move it away), then convert the parent to TabsLayout.","Convert to RowsLayout instead, which tolerates tabs children.","Restructure so no direct child has an inner tabs layout before the parent conversion."],"exampleFix":"// before — child already has inner tabs; parent->TabsLayout fails\nmutation.execute({ type: 'UPDATE_LAYOUT', payload: { path: '/rows/0', layoutType: 'TabsLayout' } });\n\n// after — neutralize the child's inner tabs first\nmutation.execute({ type: 'UPDATE_LAYOUT', payload: { path: '/rows/0/rows/0', layoutType: 'RowsLayout' } });\nmutation.execute({ type: 'UPDATE_LAYOUT', payload: { path: '/rows/0', layoutType: 'TabsLayout' } });","handlingStrategy":"validation","validationCode":"// Before converting a layout to TabsLayout, get its structure and check no direct child has inner tabs.\nconst layout = await mutation.execute({ type: 'GET_LAYOUT', payload: { path } });\n// If any direct child's inner layout is TabsLayout, convert that child to RowsLayout first.","typeGuard":null,"tryCatchPattern":"const res = await mutation.execute({ type: 'UPDATE_LAYOUT', payload });\nif (!res.success && res.error?.includes(\"a child's inner layout is already tabs\")) {\n  // first convert the offending child to RowsLayout, then retry the parent conversion\n}","preventionTips":["Before converting a container to tabs, neutralize any child whose inner layout is tabs.","Use GET_LAYOUT to inspect children before structural conversions.","Restructure top-down: fix inner nesting before converting the parent."],"tags":["layout","mutation-api","nesting","tabs","validation"],"backgroundTag":null,"analyzedSha":"ae3104e3690e28e72c8f0c97efc12647a70f75c3","analyzedAt":"2026-08-12T12:48:38.752Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}