siyuan-note/siyuan · error

document ID does not match path

Error message

document ID does not match path: %s

What it means

SiYuan requires that a document file's name equals the root block ID (filename is '<ID>.sy'). After parsing, if tree.Root.ID differs from the filename stem, the scan aborts because references would be attributed to the wrong block ID and downstream relinking/history would corrupt the index.

Solutions

  1. Rename the file back to '<RootID>.sy' matching the root ID in its JSON content.
  2. Or use the SiYuan app's duplicate/export features, which regenerate IDs, instead of copying files manually.
  3. If the document is a stray duplicate, delete it.
  4. After fixing, rerun the relink with dryRun=true to confirm the whole notebook scans cleanly.

Example fix

// before (shell)
mv data/notebooks/xxx/20240101120000-abc.sy data/notebooks/xxx/my-note.sy
// after (shell)
mv data/notebooks/xxx/my-note.sy data/notebooks/xxx/20240101120000-abc.sy
Defensive patterns

Strategy: validation

Validate before calling

var hdr struct{ ID string `json:"ID"` }
json.Unmarshal(data, &hdr)
if hdr.ID != strings.TrimSuffix(filepath.Base(syPath), ".sy") {
    return fmt.Errorf("ID/filename mismatch: %s", syPath)
}

Prevention

When it happens

Trigger: A .sy file was renamed on disk (manually or by a script) without updating the root ID inside the JSON, or the document was copied to a new filename while retaining the original ID; detected as tree.Root.ID != strings.TrimSuffix(entry.Name(), ".sy").

Common situations: Users renaming .sy files in a file manager to reorganize; duplicate-document scripts copying files without regenerating IDs; restoring backups under different filenames; git operations that renamed files.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/asset_relink.go:198

			}
			titles[header.ID] = header.Properties.Title
			if !p.mayContainReferences(data) && !bytes.Contains(data, []byte("NodeAttributeView")) {
				return nil
			}
			p.parsedDocuments++
			tree, readErr := dataparser.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
			if readErr != nil || tree == nil || tree.Root == nil {
				return fmt.Errorf("cannot parse document %s: %v", absPath, readErr)
			}
			rel, _ := filepath.Rel(boxDir, absPath)
			tree.Box, tree.Path = boxID, "/"+filepath.ToSlash(rel)
			tree.Root.Box, tree.Root.Path = tree.Box, tree.Path
			tree.HPath = "/" + tree.Root.IALAttr("title")
			if readErr = filesys.NormalizeTreeForRead(tree); readErr != nil {
				return readErr
			}
			if tree.Root.ID != strings.TrimSuffix(entry.Name(), ".sy") {
				return fmt.Errorf("document ID does not match path: %s", absPath)
			}
			titles[tree.Root.ID] = tree.Root.IALAttr("title")
			before := len(p.result.References)
			p.tree(tree, apicontract.AssetReference{Notebook: boxID, RootID: tree.Root.ID, Path: tree.Path})
			retain := false
			if len(p.result.References) > before {
				p.files = append(p.files, &assetRelinkFile{path: absPath, before: data, tree: tree, items: p.referenceItems(before)})
				retain = true
			}
			ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
				if entering && n.Type == ast.NodeAttributeView {
					retain = true
				}
				return ast.WalkContinue
			})
			if retain {
				p.trees = append(p.trees, tree)
			}

View on GitHub (pinned to 9f775e8a12)