siyuan-note/siyuan · error

Conf.Language(14) (copy assets failed: %s)

Error message

Conf.Language(14) (copy assets failed: %s)

What it means

When removeAssets is false, ExportDocx copies the temporary assets/ directory next to the exported .docx so embedded images resolve. If filelock.Copy of the assets directory to <savePath>/assets fails, the localized 'Export failed: %s' template is filled with the error and returned.

Source

Thrown at kernel/model/export.go:1175

		output, pandocErr := pandoc.CombinedOutput()
		if pandocErr != nil {
			argStr := strings.Join(args, " ")
			msg := gulu.DecodeCmdOutput(output)
			logging.LogErrorf("export docx [%s] failed: %s", argStr, msg)
			return fmt.Errorf(Conf.Language(14), msg)
		}

		fullPath = filepath.Join(savePath, name+".docx")
		fullPath = util.GetUniqueFilename(fullPath)
		if copyErr := filelock.Copy(tmpDocxPath, fullPath); copyErr != nil {
			logging.LogErrorf("export docx failed: %s", copyErr)
			return fmt.Errorf(Conf.Language(14), copyErr)
		}

		if tmpAssets := filepath.Join(tmpDir, "assets"); !removeAssets && gulu.File.IsDir(tmpAssets) {
			if copyErr := filelock.Copy(tmpAssets, filepath.Join(savePath, "assets")); copyErr != nil {
				logging.LogErrorf("export docx failed: %s", copyErr)
				return fmt.Errorf(Conf.Language(14), copyErr)
			}
		}
		return nil
	})
	if err != nil {
		fullPath = ""
	}
	return
}

func ExportMarkdownHTML(id, savePath string, docx, merge bool, mergeHeadingOptions ...MergeHeadingOptions) (name, dom string) {
	if err := prepareExportBlockAssets(id, merge); err != nil {
		util.PushErrMsg(err.Error(), 7000)
		return
	}
	name, dom, err := exportMarkdownHTML(id, savePath, docx, merge, mergeHeadingOptions...)
	if err != nil {
		logging.LogErrorf("export markdown html [%s] failed: %s", id, err)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Ensure no process locks files under <savePath>/assets (close editors, pause cloud sync) and retry.
  2. Export to a fresh directory instead of one containing a previous assets folder.
  3. Make existing destination asset files writable or remove the stale assets directory manually.
  4. Check free disk space and directory write permissions for the save path.
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: check existing assets dir is writable
const assetsDir = savePath + "/assets";
if (await dirExists(assetsDir)) await ensureWritable(assetsDir);

Try / catch

try {
  await exportDocx({id, savePath});
} catch (e) {
  if (String(e.msg).includes('assets')) {
    adviseClearingStaleAssetsDir();
  }
}

Prevention

When it happens

Trigger: filelock.Copy(tmpAssets, filepath.Join(savePath, "assets")) fails: an existing assets directory or files inside it are locked/read-only, disk full, or permission denied on the destination.

Common situations: Destination already contains an assets folder with read-only files (e.g. from a previous export under version control or cloud sync); exporting onto a read-only volume; partial previous export left locked files.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/5e1aabfd0914a418. Report an issue: GitHub.