{"record":{"id":"63b488d7f2491452","repo":"siyuan-note/siyuan","slug":"350","errorCode":"350","errorMessage":"global attachment [%s] already exists","messagePattern":"global attachment \\[(.+?)\\] already exists","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/import_obsidian.go","lineNumber":1756,"sourceCode":"\t\tmarkObsidianNotebookIncomplete(boxID, notebookName)\n\t\tfailObsidianTask(taskID, \"writing\", err, result)\n\t\tremoveObsidianTemp(taskID)\n\t\treturn\n\t}\n\tif err = os.MkdirAll(filepath.Join(util.DataDir, \"assets\"), 0755); err != nil {\n\t\tresult.Incomplete = true\n\t\tmarkObsidianNotebookIncomplete(boxID, notebookName)\n\t\tfailObsidianTask(taskID, \"writing\", err, result)\n\t\tremoveObsidianTemp(taskID)\n\t\treturn\n\t}\n\n\tfor _, key := range assetKeys {\n\t\tasset := vault.ImportAssets[key]\n\t\tsource := filepath.Join(assetsTemp, asset.FinalName)\n\t\tdestination := filepath.Join(util.DataDir, \"assets\", asset.FinalName)\n\t\tif filelock.IsExist(destination) {\n\t\t\terr = fmt.Errorf(\"global attachment [%s] already exists\", asset.FinalName)\n\t\t} else {\n\t\t\terr = filelock.Rename(source, destination)\n\t\t}\n\t\tif err != nil {\n\t\t\tresult.Incomplete = true\n\t\t\tmarkObsidianNotebookIncomplete(boxID, notebookName)\n\t\t\tfailObsidianTask(taskID, \"writing\", err, result)\n\t\t\tremoveObsidianTemp(taskID)\n\t\t\treturn\n\t\t}\n\t}\n\n\tupdateObsidianTask(taskID, ObsidianTaskStateIndexing, 95, \"Indexing notebook\")\n\texisted, err := Mount(boxID)\n\tif err != nil {\n\t\tresult.Incomplete = true\n\t\tmarkObsidianNotebookIncomplete(boxID, notebookName)\n\t\tfailObsidianTask(taskID, \"indexing\", err, result)","sourceCodeStart":1738,"sourceCodeEnd":1774,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/import_obsidian.go#L1738-L1774","documentation":"In the writing phase each staged attachment is renamed from temp into the global <workspace>/assets/ directory; if filelock.IsExist(destination) is true the task aborts with 'global attachment [name] already exists' (i18n 350), the notebook is marked incomplete via markObsidianNotebookIncomplete, and failObsidianTask records it at the 'writing' stage. The final name is the importer-computed FinalName, so this means a file with that exact name already occupies the global assets slot.","triggerScenarios":"Importing a vault whose attachment resolved name collides with an existing file in the workspace assets folder - most commonly residue from an earlier import of the same vault, an earlier attempt that failed midway after renaming some assets, or genuinely identical attachment filenames across different sources.","commonSituations":"Re-importing the same vault after a previous partial/failed run (assets already moved into data/assets); importing two different vaults that contain same-named attachments; a previous import cancelled during the writing phase leaving half-moved files.","solutions":["Remove or rename the conflicting file(s) under the workspace data/assets/ directory named in the error, then retry the import","If a prior import of the same vault already moved these attachments, either reuse that notebook or delete the earlier notebook and its assets before re-importing","Check whether a previous import task was left half-finished; clean its assets and re-analyze","If the collision is between two distinct vaults, rename one vault's attachment before import"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before confirming import, assert none of the planned FinalNames collide\nfor _, name := range plannedFinalNames {\n    if filelock.IsExist(filepath.Join(util.DataDir, \"assets\", name)) {\n        return fmt.Errorf(\"assets/%s already exists; remove or rename it first\", name)\n    }\n}","typeGuard":"func isIncompleteNotebookWrite(err error) bool {\n\tvar ue *obsidianUserError\n\treturn errors.As(err, &ue) && ue.DetailLanguage == 350\n}","tryCatchPattern":"// Not retryable as-is: clean the colliding entries under data/assets,\n// remove the incomplete notebook, then restart the vault import.","preventionTips":["Never re-import the same vault over a half-finished previous attempt without cleanup","Check data/assets for leftovers after any failed or cancelled import","Rename conflicting attachments before importing overlapping vaults"],"tags":["obsidian","attachments","name-collision","assets","import"],"backgroundTag":"file-already-exists","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}