siyuan-note/siyuan · error

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

Error message

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

What it means

When copying a resource file/folder into the temporary export directory fails, ExportResources wraps the underlying OS error with the localized template Conf.Language(14) ('copy resource failed: %s'). The original error (permissions, missing file, disk full, name collision) is embedded via fmt.Errorf so the log records the source and destination while the user sees a friendly message.

Source

Thrown at kernel/model/export.go:886

			os.Remove(zipFilePath)
			os.Remove(zipFilePath + ".partial")
		}
	}()

	// 将需要导出的文件/文件夹复制到临时文件夹
	for _, resourcePath := range resourcePaths {
		resourceFullPath := filepath.Join(util.WorkspaceDir, resourcePath) // 资源完整路径
		if !util.IsAbsPathInWorkspace(resourceFullPath) {
			logging.LogErrorf("resource path [%s] is not in workspace", resourceFullPath)
			err = errors.New("resource path [" + resourcePath + "] is not in workspace")
			return
		}

		resourceBaseName := filepath.Base(resourceFullPath)                   // 资源名称
		resourceCopyPath := filepath.Join(exportFolderPath, resourceBaseName) // 资源副本完整路径
		if err = copyExportResource(resourceFullPath, resourceCopyPath); err != nil {
			logging.LogErrorf("copy resource will be exported from [%s] to [%s] failed: %s", resourcePath, resourceCopyPath, err)
			err = fmt.Errorf(Conf.Language(14), err.Error())
			return
		}
	}

	zipPartialPath := zipFilePath + ".partial"
	zip, err := gulu.Zip.Create(zipPartialPath)
	if err != nil {
		logging.LogErrorf("create export zip [%s] failed: %s", zipFilePath, err)
		return
	}
	zipClosed := false
	defer func() {
		if !zipClosed {
			_ = zip.Close()
		}
	}()

	if err = zip.AddDirectory(zipBaseName, exportFolderPath); err != nil {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Read the wrapped %s detail to find the underlying cause (missing file, permission, disk space)
  2. Verify the source resource still exists and is readable before exporting
  3. Free disk space / check write permissions on the temp export directory
  4. Retry after resolving file locks (close apps holding the file, exclude from AV scanning)

Example fix

// before
// source may have been deleted
await exportResources(staleAssetPaths);
// after
const existing = staleAssetPaths.filter(p => fs.existsSync(path.join(workspaceDir, p)));
await exportResources(existing);
Defensive patterns

Strategy: try-catch

Validate before calling

for _, p := range paths {
    if _, err := os.Stat(filepath.Join(util.WorkspaceDir, p)); err != nil {
        return fmt.Errorf("resource missing before export: %s", p)
    }
}

Try / catch

_, err := model.ExportResources(paths, name)
if err != nil {
    var msg string
    fmt.Sscanf(err.Error(), Conf.Language(14), &msg) // unwrap %s detail
    log.Printf("export copy failed: %s", msg)
}

Prevention

When it happens

Trigger: copyExportResource returns an error for any resource in the list — source deleted between listing and copy, read permission denied, destination write failure in TempDir/export, directory walk error, or disk quota exhausted.

Common situations: Asset file removed or renamed after the export list was built; read-only or AV-scanner-locked files on Windows; temp directory on a full disk; exporting a directory containing files the process cannot read.

Related errors


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