{"record":{"id":"dcd2bb4ba8530337","repo":"siyuan-note/siyuan","slug":"target-already-exists-or-is-unreadable-s","errorCode":null,"errorMessage":"target already exists or is unreadable: %s","messagePattern":"target already exists or is unreadable: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/compare_write.go","lineNumber":23,"sourceCode":"package util\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/88250/gulu\"\n\t\"github.com/siyuan-note/filelock\"\n)\n\n// WriteFileIfUnchanged 在同一文件锁内比对扫描源并原子写入；original 为 nil 时要求目标不存在。\nfunc WriteFileIfUnchanged(path string, original, data []byte) error {\n\tfilelock.Lock(path)\n\tdefer filelock.Unlock(path)\n\tcurrent, err := os.ReadFile(path)\n\tif original == nil {\n\t\tif !os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"target already exists or is unreadable: %s\", path)\n\t\t}\n\t} else if err != nil {\n\t\treturn err\n\t} else if !bytes.Equal(current, original) {\n\t\treturn fmt.Errorf(\"source changed during asset relink: %s\", path)\n\t}\n\treturn gulu.File.WriteFileSafer(path, data, 0644)\n}\n","sourceCodeStart":5,"sourceCodeEnd":32,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/util/compare_write.go#L5-L32","documentation":"WriteFileIfUnchanged writes data to path atomically under a file lock. When original is nil the function's contract is create-if-absent: it requires the target not to exist. This error is thrown when the target file already exists on disk (or exists but cannot be read, e.g. a directory or permission problem), so an unconditional overwrite would be unsafe.","triggerScenarios":"Calling WriteFileIfUnchanged(path, nil, data) while path already exists; callers include saveAttributeView, WriteTreeIfUnchanged, apply, and Save when creating a brand-new file without a source snapshot.","commonSituations":"A create-only write racing with another process/component that created the file first (e.g. duplicate asset relink, attribute view save after the .av JSON was already written); passing nil by mistake when a source snapshot was available; the target path exists as a directory so ReadFile fails with EISDIR.","solutions":["Check os.Stat(path) before calling and skip or load the existing file when it exists","If overwriting is intended, read the current content and pass it as original instead of nil","If the file was just created by the same flow, read it and compare/write with the actual original bytes","Handle the returned error by re-reading the target and merging/retrying idempotently"],"exampleFix":"// before\nerr := util.WriteFileIfUnchanged(path, nil, data)\n// after\nif _, statErr := os.Stat(path); statErr == nil {\n    existing, _ := os.ReadFile(path)\n    err = util.WriteFileIfUnchanged(path, existing, data)\n} else {\n    err = util.WriteFileIfUnchanged(path, nil, data)\n}","handlingStrategy":"validation","validationCode":"func canCreate(path string) error {\n    if _, err := os.Stat(path); err == nil {\n        return fmt.Errorf(\"target exists: %s\", path)\n    } else if !os.IsNotExist(err) {\n        return err\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"err := util.WriteFileIfUnchanged(path, nil, data)\nif err != nil && strings.Contains(err.Error(), \"target already exists\") {\n    // load existing content and merge or skip\n}","preventionTips":["Always stat the target before a create-only write","Pass a real snapshot as original whenever one exists instead of nil","Treat create-only writes as idempotent: on this error, read the existing file and decide"],"tags":["filesystem","concurrency","create-only-write"],"backgroundTag":"file-already-exists","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"}