{"record":{"id":"35c88afeb0ccd7c7","repo":"siyuan-note/siyuan","slug":"copy-asset-s-to-s-failed-w","errorCode":null,"errorMessage":"copy asset [%s] to [%s] failed: %w","messagePattern":"copy asset \\[(.+?)\\] to \\[(.+?)\\] failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/history.go","lineNumber":907,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"get history directory failed: %w\", err)\n\t}\n\n\tfor _, file := range assets {\n\t\tassetRelPath, relErr := filepath.Rel(filepath.Join(util.DataDir, \"assets\"), file)\n\t\tif relErr != nil || assetRelPath == \".\" || strings.HasPrefix(assetRelPath, \"..\"+string(filepath.Separator)) {\n\t\t\treturn errors.New(\"asset path must be under assets\")\n\t\t}\n\t\thistoryPath := filepath.Join(historyDir, \"assets\", assetRelPath)\n\t\tif err = os.MkdirAll(filepath.Dir(historyPath), 0755); err != nil {\n\t\t\treturn fmt.Errorf(\"create history directory [%s] failed: %w\", filepath.Dir(historyPath), err)\n\t\t}\n\n\t\tif err = filelock.Copy(file, historyPath); err != nil {\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"copy asset [%s] to [%s] failed: %w\", file, historyPath, err)\n\t\t}\n\t}\n\n\tindexHistoryDir(filepath.Base(historyDir), util.NewLute())\n\treturn\n}\n\nfunc (box *Box) generateDocHistory0() {\n\tfiles := box.recentModifiedDocs()\n\tif 1 > len(files) {\n\t\treturn\n\t}\n\n\thistoryDir, err := getHistoryDir(HistoryOpUpdate)\n\tif err != nil {\n\t\tlogging.LogErrorf(\"get history dir failed: %s\", err)\n\t\treturn\n\t}","sourceCodeStart":889,"sourceCodeEnd":925,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/history.go#L889-L925","documentation":"createAssetsHistory (kernel/model/history.go:903-908) fails when filelock.Copy of the asset into the history tree errors with anything other than os.IsNotExist (missing sources are deliberately skipped with continue). The %w preserves the real I/O error: permission denied on source or destination, disk full mid-copy, or a file held with an exclusive lock by another process (typical on Windows).","triggerScenarios":"The source asset is unreadable (mode 000, ownership mismatch), the destination history dir became read-only, ENOSPC hits mid-copy of a large media file, or an antivirus/indexer/other program holds the asset open exclusively on Windows.","commonSituations":"AV scanners or sync clients locking asset files on Windows; Docker volume permission drift; disks filling during snapshots of large video/audio assets.","solutions":["Read the wrapped error (errors.Is fs.ErrPermission / fs.ErrNoSpace etc.) and fix that specific condition","Verify read permission on the source asset and write permission on the history destination","Free disk space and retry; snapshot generation runs periodically and will succeed once space exists","On Windows, close or exclude the workspace from programs holding exclusive locks, then retry"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if fi, err := os.Stat(file); err != nil {\n    return err\n} else if fi.Mode().Perm()&0400 == 0 {\n    return fmt.Errorf(\"asset not readable: %s\", file)\n}","typeGuard":null,"tryCatchPattern":"var err error\nfor i := 0; i < 3; i++ {\n    if err = createAssetsHistory(files); err == nil {\n        break\n    }\n    if !errors.Is(err, fs.ErrPermission) && !errors.Is(err, fs.ErrTemporary) {\n        break // permanent error, inspect wrapped cause\n    }\n    time.Sleep(time.Duration(i+1) * time.Second)\n}","preventionTips":["On Windows, exclude the workspace from AV/sync tools that take exclusive locks","Keep source assets readable (0644) and history/ writable","Treat permission and no-space wrapped errors as permanent, locks as transient"],"tags":["io","copy","file-lock","windows","disk-full","permissions"],"backgroundTag":"file-copy-io-error","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}