siyuan-note/siyuan · error
Please configure [Settings - Export - Pandoc - Path to…
Error message
Please configure [Settings - Export - Pandoc - Path to Pandoc executable] first
What it means
model.ExportDocx (POST /api/export/exportDocx) shells out to pandoc. It resolves the binary via util.GetPandocRuntime and validates it with util.IsValidPandocBin; if invalid it re-initializes from Conf.Export.PandocBin and re-checks, and if still invalid returns the localized error asking for Settings - Export - Pandoc - Path to Pandoc executable (Conf.Language(115)). No conversion is attempted without a working pandoc.
Solutions
- Install pandoc (winget/choco/brew/apt) or download the official release
- Set the absolute binary path in Settings - Export - Pandoc - Path to Pandoc executable, or clear it to let the kernel auto-detect from PATH
- Verify as the same user that runs the kernel: <path-to-pandoc> --version
- On Linux chmod +x the binary; on macOS remove the quarantine attribute (xattr -d com.apple.quarantine <binary>) if Gatekeeper blocks it
- Retry the export — the kernel re-inits pandoc from config on each call
Example fix
# before: ExportDocx -> "Please configure [Settings - Export - Pandoc - Path to Pandoc executable] first" # after: install, configure, retry brew install pandoc # or: choco install pandoc / apt install pandoc # Settings - Export - Pandoc - Path to Pandoc executable: /opt/homebrew/bin/pandoc # then retry POST /api/export/exportDocx
Defensive patterns
Strategy: validation
Validate before calling
if !util.IsValidPandocBin(util.GetPandocRuntime().BinPath) {
util.InitPandoc(model.Conf.Export.PandocBin)
if !util.IsValidPandocBin(util.GetPandocRuntime().BinPath) {
return errors.New("configure a valid pandoc binary before docx export")
}
}
fullPath, err := model.ExportDocx(id, savePath, false, false) Try / catch
if _, err := model.ExportDocx(id, savePath, false, false); err != nil {
if err.Error() == model.Conf.Language(115) {
// pandoc missing: prompt to install/configure, then let the user retry
}
} Prevention
- Validate pandoc at app or plugin startup, not at export time
- Pin an absolute pandoc path for portable installs
- Smoke-test the binary with --version after OS updates
When it happens
Trigger: POST /api/export/exportDocx when pandoc is not installed, Conf.Export.PandocBin points to a missing or non-executable file, or the binary exists but fails the validity invocation (wrong architecture, blocked by macOS Gatekeeper).
Common situations: Fresh installs without pandoc; Docker/minimal images; portable setups where the configured path went stale after an update; mis-entered paths with quotes or spaces; downloaded binaries lacking the exec bit on Linux.
Related errors
- Conf.Language(115)
- Conf.Language(115) (Pandoc binary invalid)
- Conf.Language(14) (pandoc output: )
- Conf.Language(14) (copy assets failed: )
- Conf.Language(14) (copy docx failed: )
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/7fbe6f3ab6bd153a.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/export.go:1054
if footnotesDefBlock := tree.Root.ChildByType(ast.NodeFootnotesDefBlock); nil != footnotesDefBlock {
footnotesDefBlock.Unlink()
}
return nil
}); exportErr != nil {
logging.LogErrorf("export preview [%s] failed: %s", id, exportErr)
return
}
return
}
func ExportDocx(id, savePath string, removeAssets, merge bool, mergeHeadingOptions ...MergeHeadingOptions) (fullPath string, err error) {
err = withExportReadLockByBlockID(id, func() error {
pandocRuntime := util.GetPandocRuntime()
if !util.IsValidPandocBin(pandocRuntime.BinPath) {
util.InitPandoc(Conf.Export.PandocBin)
pandocRuntime = util.GetPandocRuntime()
if !util.IsValidPandocBin(pandocRuntime.BinPath) {
return errors.New(Conf.Language(115))
}
}
tmpDir := filepath.Join(util.TempDir, "export", gulu.Rand.String(7))
if bt := getExportBlockTree(id); bt != nil && IsEncryptedBox(bt.BoxID) {
exportID, idErr := newManagedEncryptedExportID()
if idErr != nil {
return idErr
}
tmpDir = filepath.Join(util.TempDir, "export", bt.BoxID, "docx", exportID)
}
if mkdirErr := os.MkdirAll(tmpDir, 0755); mkdirErr != nil {
return mkdirErr
}
defer os.RemoveAll(tmpDir)
name, content := ExportMarkdownHTML(id, tmpDir, true, merge, mergeHeadingOptions...)
content = strings.ReplaceAll(content, " \n", "<br>\n")
View on GitHub (pinned to afa823b6b4)