{"record":{"id":"a16d6f1249fca77d","repo":"siyuan-note/siyuan","slug":"table-cell-rich-text-projection-length-changed","errorCode":null,"errorMessage":"table cell rich text projection length changed","messagePattern":"table cell rich text projection length changed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/treenode/table_cell_rich.go","lineNumber":179,"sourceCode":"\t\t\t\t\t}\n\t\t\t\t\tswitch origin.Type {\n\t\t\t\t\tcase ast.NodeCodeBlockCode:\n\t\t\t\t\t\torigin.Tokens = []byte(html.UnescapeString(actual.TextMarkTextContent) + \"\\n\")\n\t\t\t\t\tcase ast.NodeMathBlockContent:\n\t\t\t\t\t\torigin.Tokens = []byte(html.UnescapeString(actual.TextMarkInlineMathContent))\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tparent, previous, next, first, last := origin.Parent, origin.Previous, origin.Next, origin.FirstChild, origin.LastChild\n\t\t\t\t\t\t*origin = *parse.CloneTableCellInline(actual)\n\t\t\t\t\t\torigin.Parent, origin.Previous, origin.Next, origin.FirstChild, origin.LastChild = parent, previous, next, first, last\n\t\t\t\t\t}\n\t\t\t\t\tchanged = true\n\t\t\t\t}\n\t\t\t\tif e := sync(want.FirstChild, actual.FirstChild); nil != e {\n\t\t\t\t\treturn e\n\t\t\t\t}\n\t\t\t}\n\t\t\tif want != nil || actual != nil {\n\t\t\t\treturn fmt.Errorf(\"table cell rich text projection length changed\")\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tif nil != cell.FirstChild {\n\t\t\tif err = sync(expected.FirstChild, cell.FirstChild); nil != err {\n\t\t\t\treturn ast.WalkStop\n\t\t\t}\n\t\t}\n\t\tif changed {\n\t\t\tvar content string\n\t\t\tif content, err = av.RenderTableCellRich(fragment); nil != err {\n\t\t\t\treturn ast.WalkStop\n\t\t\t}\n\t\t\tupdates[cell] = content\n\t\t}\n\t\treturn ast.WalkSkipChildren\n\t})\n\tif nil != err {","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/treenode/table_cell_rich.go#L161-L197","documentation":"The recursive sync over a table cell's projected inline nodes walks both the expected projection and the actual cell children in lockstep. This error means one list ended before the other — the actual cell contains fewer or more inline nodes than the expected projection, so positions cannot be matched.","triggerScenarios":"An edit inside a projected table cell added or deleted inline nodes (e.g. new text spans, removed math element) so the child counts differ; programmatic tree mutation without regenerating the projection.","commonSituations":"Plugin DOM/tree manipulation of table cells; paste operations into projected cells via older clients; concurrent edits merged by sync that changed node counts.","solutions":["Re-parse/re-render the document to rebuild the projection from the cell's source","Undo the recent edit to the table cell (Ctrl+Z) or restore from block history","Update clients/plugins that mutate table cell AST directly to regenerate projections after edits","Restore the document from sync snapshot if corruption persists"],"exampleFix":"// before: cell has an extra inline node vs projection\n// after: regenerate expected projection from the current fragment\nexpected, sources := parse.ProjectTableCellRichWithSources(fragment)\nif nil != cell.FirstChild { sync(expected.FirstChild, cell.FirstChild) }","handlingStrategy":"validation","validationCode":"if expected.ChildCount() != cell.ChildCount() {\n  expected, sources = parse.ProjectTableCellRichWithSources(fragment)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Count and compare children before syncing projections","Regenerate projections after any programmatic tree edit","Update plugins that insert/remove inline nodes in table cells"],"tags":["ast","table","structure"],"backgroundTag":"internal-invariant-violation","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}