{"record":{"id":"96cce1276108cf82","repo":"siyuan-note/siyuan","slug":"workspace-changed-during-asset-scan-retry-the-request-s","errorCode":null,"errorMessage":"workspace changed during asset scan; retry the request: %s","messagePattern":"workspace changed during asset scan; retry the request: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/asset_relink_batch.go","lineNumber":242,"sourceCode":"\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.inventory[path] = assetRelinkStamp{true, info.Size(), info.ModTime(), info.Mode()}\n\treturn nil\n}\n\nfunc (p *assetRelinkPlan) validateSnapshot() error {\n\tfor path, stamp := range p.inventory {\n\t\tif err := p.checkContext(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tinfo, err := os.Stat(path)\n\t\tif !stamp.exists && os.IsNotExist(err) {\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil || !stamp.exists || stamp.size != info.Size() || !stamp.modified.Equal(info.ModTime()) || stamp.mode != info.Mode() {\n\t\t\treturn fmt.Errorf(\"workspace changed during asset scan; retry the request: %s\", path)\n\t\t}\n\t}\n\treturn nil\n}\n\n// mayContainReferences 仅排除没有目标文件名且没有任何转义的源；转义一律进入语义解析以防漏筛。\nfunc (p *assetRelinkPlan) mayContainReferences(data []byte) bool {\n\tif bytes.ContainsAny(data, \"\\\\%&\") {\n\t\treturn true\n\t}\n\treturn len(p.matcher.SearchIndexed(string(data))) > 0\n}\n\nfunc (p *assetRelinkPlan) itemsForPath(path string) map[*assetRelinkItem]bool {\n\tret := map[*assetRelinkItem]bool{}\n\tif item := p.itemByPath[path]; item != nil {\n\t\tret[item] = true\n\t}","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/asset_relink_batch.go#L224-L260","documentation":"During scanning, the plan snapshots stat stamps (existence, size, mtime, mode) of the files it will rewrite. validateSnapshot re-checks them before apply; if any file changed between scan and apply, or appeared/disappeared unexpectedly, the plan aborts with this error to avoid rewriting stale in-memory content over newer data.","triggerScenarios":"Any file targeted by the scan is modified, moved, replaced, or deleted between the scan phase and apply(); another client or sync process edits documents or assets during a long-running batch scan.","commonSituations":"Sync engine downloading changes mid-scan; a second SiYuan client editing the same workspace; user edits documents while a large batch scan is running; system clock or filesystem touching mtimes (e.g. backup tools).","solutions":["Re-run the whole relink request so the scan and apply happen back-to-back on unchanged data","Pause sync and close other clients editing the workspace during the batch operation","Retry with exponential backoff in automation, and reduce batch size to shorten the scan window","Exclude the workspace from backup/AV tools that rewrite mtimes during the operation"],"exampleFix":"// before\n_, err := model.RelinkAssets(ctx, bigBatch, false) // long scan, files change mid-way\n// after\nchunks := chunk(mappings, 20) // smaller windows\nfor _, c := range chunks {\n\tif _, err := model.RelinkAssets(ctx, c, false); err != nil {\n\t\treturn retryWithBackoff(c) // rescan on 'workspace changed'\n\t}\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"result, err := model.RelinkAssets(ctx, mappings, dryRun)\nif err != nil && strings.Contains(fmt.Sprint(err), \"workspace changed\") {\n\ttime.Sleep(time.Second)\n\tresult, err = model.RelinkAssets(ctx, mappings, dryRun) // fresh scan+apply\n}","preventionTips":["Keep the scan window short (smaller batches)","Pause sync and close other clients during relink","Avoid touching the workspace while a relink is in flight"],"tags":["go","concurrency","optimistic-locking","race-condition"],"backgroundTag":"checksum-mismatch","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}