siyuan-note/siyuan · error

no such file or directory

Error message

no such file or directory: %s

What it means

RollbackRepoSnapshotFile fails with 'no such file or directory: <temp path>' when the snapshot .sy content was written to the temp rollback directory but loadTree cannot parse it into a valid tree (nil tree). Although the message says 'no such file or directory', the usual cause is a corrupt/empty/unparseable snapshot file rather than a genuinely missing temp file.

Solutions

  1. Inspect the temp file content printed in the log message; if empty or truncated, the snapshot is corrupt
  2. Try rolling back to an earlier snapshot of the same document instead
  3. Run repo consistency check / re-sync (rebuild snapshots) if the repo was corrupted by disk failure
  4. Restore the workspace from backup if multiple snapshots fail to load
  5. Report to maintainers with the snapshot path if a valid-looking snapshot consistently fails to load (possible parser regression)

Example fix

// no code fix — pick a healthy snapshot
// before: rollback to corrupt snapshot S_N fails
// after: list history and roll back to the previous good snapshot
POST /api/repo/rollbackRepoFile {"id": "<earlierSnapshotFileID>"}
Defensive patterns

Strategy: retry

Validate before calling

// check the snapshot file is a parseable document before rollback
const raw = await fetchPost("/api/repo/getRepoFile", {id: fileID});
if (!raw.data || !String(raw.data.content).trim()) {
  throw new Error("snapshot content is empty/corrupt; choose another snapshot");
}

Try / catch

if err := model.RollbackRepoSnapshotFile(fileID); err != nil {
    if strings.HasPrefix(err.Error(), "no such file or directory:") {
        // snapshot unparseable — pick an earlier snapshot or rebuild repo
    }
    return err
}

Prevention

When it happens

Trigger: repoFileRollback on a .sy file whose decrypted content in Temp/repo/rollback/... fails to load as a Kramdown tree — empty data, truncated snapshot, or content that Lute cannot parse as a document.

Common situations: Snapshot created by an older SiYuan version with a format the current Lute rejects; interrupted sync leaving a truncated snapshot; repo corruption after disk failure or manual tampering with .siyuan/repo; a snapshot of an empty/invalid document.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/repository.go:326

		}
		boxID = box.ID

		var destPath, parentHPath string
		rootID := util.GetTreeID(file.Path)
		workingDoc := treenode.GetBlockTree(rootID)
		if needResetTree {
			workingDoc = nil
		}
		destPath, parentHPath, err = getRollbackDockPath(boxID, file.Path, workingDoc)
		if err != nil {
			return
		}

		tree, _ := loadTree(from, util.NewLute())
		if nil == tree {
			msg := fmt.Sprintf("no such file or directory: %s", from)
			logging.LogError(msg)
			err = errors.New(msg)
			return
		}

		tree.Box = boxID
		tree.Path = filepath.ToSlash(strings.TrimPrefix(destPath, util.DataDir+string(os.PathSeparator)+boxID))
		tree.HPath = parentHPath + "/" + tree.Root.IALAttr("title")
		if needResetTree {
			resetTree(tree, "", true)
		}

		if nil != workingDoc && "d" == workingDoc.Type {
			workingDocPath := filepath.Join(util.DataDir, boxID, workingDoc.Path)
			if err = filelock.Remove(workingDocPath); err != nil {
				return
			}
			logging.LogInfof("removed working doc file [%s]", workingDocPath)
		}
		if nil != workingDoc {

View on GitHub (pinned to 9f775e8a12)