siyuan-note/siyuan · error

parse tree [%s] failed

Error message

parse tree [%s] failed

What it means

Thrown when importing a single Markdown file (the non-directory branch of the import flow) and the Lute parser fails to produce an AST. parseStdMd calls parse.Parse() from the 88250/lute engine; if that returns a nil tree the file is considered unparseable as standard Markdown. The error message embeds the local file path so the offending file is identifiable. This is a hard stop — no document is created for the file.

Source

Thrown at kernel/model/import.go:1504

		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 251596fc0d)

Solutions

  1. Open the file named in the error message in a text editor and confirm it is valid UTF-8 Markdown; fix or remove corrupt content.
  2. Check the file is not zero-length or binary: run `file <localPath>` and verify it reports ASCII/UTF-8 text.
  3. If the file contains YAML front matter, validate its syntax (correct `---` delimiters, no tabs where spaces are required).
  4. As a workaround, re-save the file as clean Markdown or convert it through an external tool (e.g. pandoc) before importing.

Example fix

// before: a zero-byte or binary file named notes.md is imported, producing nil tree
data, err := os.ReadFile(localPath)
// ...
tree, yfmRootID, yfmTitle, yfmUpdated := parseStdMd(data)
if nil == tree {
    msg := fmt.Sprintf("parse tree [%s] failed", localPath)
    logging.LogError(msg)
    return errors.New(msg)
}

// after: validate the file is non-empty text before attempting parse
if len(bytes.TrimSpace(data)) == 0 {
    return fmt.Errorf("file [%s] is empty, nothing to import", localPath)
}
tree, yfmRootID, yfmTitle, yfmUpdated := parseStdMd(data)
Defensive patterns

Strategy: validation

Validate before calling

// Validate the file is non-empty, valid UTF-8 text before calling the import path
func validateMarkdownImport(localPath string) error {
    info, err := os.Stat(localPath)
    if err != nil { return fmt.Errorf("stat: %w", err) }
    if info.Size() == 0 { return errors.New("file is empty") }
    data, err := os.ReadFile(localPath)
    if err != nil { return err }
    if !utf8.Valid(data) { return errors.New("file is not valid UTF-8") }
    return nil
}

Try / catch

// Wrap the import call and surface a user-friendly message on parse failure
result, err := importData(localPath)
if err != nil && strings.Contains(err.Error(), "parse tree [") {
    return fmt.Errorf("the file could not be parsed as Markdown; check it is valid UTF-8 text: %w", err)
}

Prevention

When it happens

Trigger: Importing a single .md/.markdown file whose content Lute cannot parse into a tree (e.g. a file containing only invalid bytes, a null-laden or binary file with a .md extension, or content that triggers a parser-level nil return). Triggered via the import-data API path that handles individual file imports, not the directory-walk branch.

Common situations: A .md file that is actually binary or zero-byte; a file with encoding issues or embedded NUL bytes; a corrupted download renamed to .md; an empty file that the Lute engine declines to wrap in a tree; a file with malformed YAML front matter that confuses the YFM-enabled parser.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/051f0f2a58376e73. Report an issue: GitHub.