siyuan-note/siyuan · error

table cell rich text payload is not UTF-8

Error message

table cell rich text payload is not UTF-8

What it means

While validating a serialized document that carries table-cell rich text (checkTableCellRichJSON), the TableCellRich JSON payload attached to a NodeTableCell must be valid UTF-8. If it contains invalid byte sequences it cannot be safely parsed, so validation fails before any further checks.

Solutions

  1. Restore the document from a snapshot/history or backup; the payload is corrupt at the byte level.
  2. Inspect the .sy file and fix/remove the malformed TableCellRich field for that cell.
  3. If produced by an external tool, fix that tool to emit valid UTF-8 JSON before re-importing.
Defensive patterns

Strategy: validation

Validate before calling

if !utf8.Valid(tableCellRichBytes) { /* reject or restore from backup before loading the document */ }

Type guard

func validUTF8(b []byte) bool { return utf8.Valid(b) }

Prevention

When it happens

Trigger: Loading or importing a document whose .sy JSON contains a NodeTableCell with a TableCellRich field containing non-UTF-8 bytes — typically from binary corruption, bad encoding during external editing, or a broken third-party generator.

Common situations: Documents edited or generated outside SiYuan with wrong encoding, files corrupted by non-UTF-8-safe tools, or data synced from a source that mangled bytes.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at kernel/treenode/table_cell_rich.go:33

const TableCellRichDocumentSpec = "4"
const TableCellRichTableAttribute = "custom-sy-table-rich"

func checkTableCellRichJSON(data []byte, spec string) error {
	type rawNode struct {
		Type          string
		TableCellRich json.RawMessage
		Children      []json.RawMessage
	}
	var check func(json.RawMessage, string) error
	check = func(raw json.RawMessage, context string) error {
		var node rawNode
		if err := json.Unmarshal(raw, &node); nil != err {
			return err
		}
		if len(node.TableCellRich) > 0 {
			if !utf8.Valid(node.TableCellRich) {
				return fmt.Errorf("table cell rich text payload is not UTF-8")
			}
			version, _ := strconv.Atoi(spec)
			if version < 4 || node.Type != "NodeTableCell" || context != "row" {
				return fmt.Errorf("invalid table cell rich text document spec [%s]", spec)
			}
			var fields map[string]json.RawMessage
			if err := json.Unmarshal(node.TableCellRich, &fields); nil != err || len(fields) != 3 {
				return fmt.Errorf("invalid table cell rich text payload")
			}
			for _, name := range []string{"spec", "format", "content"} {
				if len(fields[name]) == 0 || bytes.Equal(bytes.TrimSpace(fields[name]), []byte("null")) {
					return fmt.Errorf("missing table cell rich text field [%s]", name)
				}
			}
			var rich ast.TableCellRich
			if err := json.Unmarshal(node.TableCellRich, &rich); nil != err {
				return err
			}

View on GitHub (pinned to 9f775e8a12)