siyuan-note/siyuan · error · obsidianUserError

347

347

Error message

parse Markdown [%s] failed

What it means

First analysis pass: parseStdMd(data) returned a nil parse.Tree for a markdown file. Reported with language code 347 ('Unable to convert Markdown file [%s]'). parseStdMd parses the file as standard (non-SiYuan) Markdown via Lute; a nil tree means Lute rejected the input outright rather than producing a partial AST. This blocks heading/block-ID indexing for that file.

Source

Thrown at kernel/model/import_obsidian.go:876

			return newObsidianReadUserError(doc.Source, err)
		}
		if !utf8.Valid(data) {
			return newObsidianUserError(344, doc.Source.RelPath,
				fmt.Errorf("Markdown [%s] is not valid UTF-8", doc.Source.RelPath))
		}
		scan := scanObsidianSource(data)
		for _, blockID := range scan.BlockIDs {
			if scan.Duplicates[blockID] {
				doc.DuplicateBlocks[blockID] = true
			}
			if doc.BlockIDs[blockID] == "" {
				doc.BlockIDs[blockID] = ast.NewNodeID()
			}
		}
		tree, _, _, _ := parseStdMd(data)
		if tree == nil {
			return newObsidianUserError(347, doc.Source.RelPath,
				fmt.Errorf("parse Markdown [%s] failed", doc.Source.RelPath))
		}
		buildObsidianHeadingIndex(doc, tree)
		vault.Analysis.WikiLinkCount += countObsidianNonEmbedTokens(scan.Wikis)
		vault.Analysis.EmbedCount += countObsidianEmbedTokens(scan.Wikis)
		vault.Analysis.CommentCount += len(scan.Comments)
		vault.Analysis.FootnoteCount += scan.Footnotes
		vault.Analysis.BlockIDCount += len(scan.BlockIDs)
		processed++
		progress(35+processed*30/maxInt(sourceCount, 1), "Analyzing Markdown syntax")
	}

	for _, doc := range vault.Docs {
		if doc.Synthetic {
			continue
		}
		if err := ctx.Err(); err != nil {
			return err
		}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Open the reported file in a Markdown linter/editor and fix or simplify the offending construct (unclosed code fences, runaway nesting).
  2. If the file is not real Markdown, rename it to a non-.md extension or move it out of the vault.
  3. Trim/truncate an absurdly large single file and retry analysis; if it then parses, narrow down the offending region.
  4. Check the kernel log for the underlying Lute error/stack to identify the exact construct, then file a Lute issue with a minimal reproducer.

Example fix

// before: file with an unterminated code fence spanning the whole doc
//   ```python
//   ... 10MB of content with no closing fence

// after: close the fence (or split the file)
//   ```python
//   ...
//   ```
Defensive patterns

Strategy: validation

Validate before calling

// Pre-parse each markdown with a lenient parser/linter to catch hard failures.
func vaultMarkdownParses(root string) (string, error) {
    var first string
    err := filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
        if err != nil || d.IsDir() || !strings.HasSuffix(p, ".md") { return err }
        b, e := os.ReadFile(p); if e != nil { return e }
        if _, e := parseStdMd(b); e != nil || treeIsNil { first = p; return errors.New("unparseable") }
        return nil
    })
    return first, err
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Severe Markdown corruption that defeats the parser: file containing mostly binary/null bytes that nonetheless passed UTF-8 validation, extremely deep nesting exceeding parser limits, or a Lute internal panic recovered into a nil tree. Rare in practice because most malformed Markdown still yields a permissive AST.

Common situations: A .md file that is UTF-8 but structurally pathological (megabytes of unclosed brackets/fences, deeply nested blockquote/list stacks); a file that is actually a different format renamed to .md; a Lute version regression that fails on a specific construct.

Related errors


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