siyuan-note/siyuan · error

table cell rich text projection marker changed

Error message

table cell rich text projection marker changed

What it means

While syncing a table cell rich text projection back to its origin node, an inline signature mismatch was found but the expected node has no entry in the sources map, so the kernel cannot locate the origin node to write content back to. This indicates the projection's marker (the mapping from projected node to origin) was lost or altered.

Solutions

  1. Re-open and re-render the document so markers are regenerated
  2. Restore the affected block from data history (right-click block - history) or sync snapshots
  3. Avoid editing projected table cell content outside the editor; edit the source code/math block instead
  4. If a plugin caused it, disable the plugin and repair the block

Example fix

// before: sync without regenerated sources
sync(expected.FirstChild, cell.FirstChild) // sources map stale
// after: rebuild projection + sources before sync
expected, sources := parse.ProjectTableCellRichWithSources(fragment)
Defensive patterns

Strategy: validation

Validate before calling

expected, sources := parse.ProjectTableCellRichWithSources(fragment)
for n := expected.FirstChild; n != nil; n = n.Next {
  if sources[n] == nil { reparse() } // marker would be missing
}

Prevention

When it happens

Trigger: The projected inline node's signature differs from the origin and sources[want] is nil — e.g. the projection markers were stripped by an external edit, or the fragment was regenerated without ProjectTableCellRichWithSources before syncing.

Common situations: External tools rewriting table cell HTML/markdown and dropping internal marker attributes; data migrations between document spec versions; custom scripts manipulating .sy files.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/fa5e3b1b62cea0b1. Report an issue: GitHub.

Appendix: source

Thrown at kernel/treenode/table_cell_rich.go:160

		if !entering || nil == cell.TableCellRich {
			return ast.WalkContinue
		}
		var fragment *parse.Tree
		if fragment, err = av.ParseTableCellRich(cell.TableCellRich); nil != err {
			return ast.WalkStop
		}
		expected, sources := parse.ProjectTableCellRichWithSources(fragment)
		changed := false
		var sync func(*ast.Node, *ast.Node) error
		sync = func(want, actual *ast.Node) error {
			for ; want != nil && actual != nil; want, actual = want.Next, actual.Next {
				if want.Type != actual.Type {
					return fmt.Errorf("table cell rich text projection structure changed")
				}
				if !bytes.Equal(tableCellInlineSignature(want), tableCellInlineSignature(actual)) {
					origin := sources[want]
					if nil == origin {
						return fmt.Errorf("table cell rich text projection marker changed")
					}
					switch origin.Type {
					case ast.NodeCodeBlockCode:
						origin.Tokens = []byte(html.UnescapeString(actual.TextMarkTextContent) + "\n")
					case ast.NodeMathBlockContent:
						origin.Tokens = []byte(html.UnescapeString(actual.TextMarkInlineMathContent))
					default:
						parent, previous, next, first, last := origin.Parent, origin.Previous, origin.Next, origin.FirstChild, origin.LastChild
						*origin = *parse.CloneTableCellInline(actual)
						origin.Parent, origin.Previous, origin.Next, origin.FirstChild, origin.LastChild = parent, previous, next, first, last
					}
					changed = true
				}
				if e := sync(want.FirstChild, actual.FirstChild); nil != e {
					return e
				}
			}
			if want != nil || actual != nil {

View on GitHub (pinned to 9f775e8a12)