siyuan-note/siyuan · error

parse tree [ ] failed

Error message

parse tree [%s] failed

What it means

After reading the file, `importFromLocalPath` parses it with `parseStdMd` (Lute). If the parser returns a nil tree, the kernel logs and returns `parse tree [<localPath>] failed` to the caller. This means the file content could not be built into a valid Markdown AST — typically an unreadable/empty file or content Lute rejected at the parse stage.

Solutions

  1. Verify the file is real UTF-8 Markdown text (`file`, `head`, or opening it in an editor); re-export if it is binary or wrong encoding
  2. Check the kernel log for the logged `parse tree [...] failed` line to confirm which path failed, then inspect that exact file
  3. Convert the file to UTF-8 and remove corrupted content, then retry the import

Example fix

// before
// importing a UTF-16 encoded file exported by another tool
importFromLocalPath("/notes/doc.md", toPath) // parse tree [/notes/doc.md] failed
// after
// re-save the file as UTF-8 plain Markdown, then import
importFromLocalPath("/notes/doc-utf8.md", toPath)
Defensive patterns

Strategy: validation

Validate before calling

const buf = await fs.readFile(localPath);
const text = buf.toString("utf8");
if (text.length === 0 || /\u0000/.test(text.slice(0, 512))) {
  throw new Error("File is empty or binary; not importable Markdown");
}

Type guard

function looksLikeTextMarkdown(buf: Buffer): boolean {
  return buf.length > 0 && !buf.subarray(0, 512).includes(0);
}

Try / catch

try {
  await api.importFromLocalPath(localPath, toPath);
} catch (e) {
  if (String(e.message).startsWith("parse tree")) {
    // re-encode file to UTF-8 and retry, or skip and report to user
  }
}

Prevention

When it happens

Trigger: Calling the import API with an `.md`/`.markdown` file whose bytes produce a nil tree from `parseStdMd` — e.g. empty file, binary content with an `.md` extension, or an encoding Lute cannot handle.

Common situations: Renamed binary files (PDF/zip renamed to `.md`); zero-byte exports from other tools; files in non-UTF-8 encodings (GBK, UTF-16) whose bytes break parsing; corrupted downloads.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/import.go:1805

		if !strings.HasSuffix(fileName, ".md") && !strings.HasSuffix(fileName, ".markdown") {
			return errors.New(Conf.Language(79))
		}

		title := strings.TrimSuffix(fileName, ".markdown")
		title = strings.TrimSuffix(title, ".md")
		targetPath := strings.TrimSuffix(toPath, ".sy")
		id := ast.NewNodeID()
		targetPath = path.Join(targetPath, id+".sy")
		var data []byte
		data, err = os.ReadFile(localPath)
		if err != nil {
			return err
		}
		tree, yfmRootID, yfmTitle, yfmUpdated := parseStdMd(data)
		if nil == tree {
			msg := fmt.Sprintf("parse tree [%s] failed", localPath)
			logging.LogError(msg)
			return errors.New(msg)
		}

		if "" != yfmRootID {
			id = yfmRootID
		}
		if "" != yfmTitle {
			title = yfmTitle
		}
		unescapedTitle, unescapeErr := url.PathUnescape(title)
		if nil == unescapeErr {
			title = unescapedTitle
		}
		updated := yfmUpdated
		fname := path.Base(targetPath)
		targetPath = strings.ReplaceAll(targetPath, fname, id+".sy")

		tree.ID = id
		tree.Root.ID = id

View on GitHub (pinned to 9f775e8a12)