siyuan-note/siyuan · error
350
350
Error message
global attachment [%s] already exists
What it means
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.
Source
Thrown at kernel/model/import_obsidian.go:1756
markObsidianNotebookIncomplete(boxID, notebookName)
failObsidianTask(taskID, "writing", err, result)
removeObsidianTemp(taskID)
return
}
if err = os.MkdirAll(filepath.Join(util.DataDir, "assets"), 0755); err != nil {
result.Incomplete = true
markObsidianNotebookIncomplete(boxID, notebookName)
failObsidianTask(taskID, "writing", err, result)
removeObsidianTemp(taskID)
return
}
for _, key := range assetKeys {
asset := vault.ImportAssets[key]
source := filepath.Join(assetsTemp, asset.FinalName)
destination := filepath.Join(util.DataDir, "assets", asset.FinalName)
if filelock.IsExist(destination) {
err = fmt.Errorf("global attachment [%s] already exists", asset.FinalName)
} else {
err = filelock.Rename(source, destination)
}
if err != nil {
result.Incomplete = true
markObsidianNotebookIncomplete(boxID, notebookName)
failObsidianTask(taskID, "writing", err, result)
removeObsidianTemp(taskID)
return
}
}
updateObsidianTask(taskID, ObsidianTaskStateIndexing, 95, "Indexing notebook")
existed, err := Mount(boxID)
if err != nil {
result.Incomplete = true
markObsidianNotebookIncomplete(boxID, notebookName)
failObsidianTask(taskID, "indexing", err, result)View on GitHub (pinned to afa823b6b4)
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
Defensive patterns
Strategy: validation
Validate before calling
// Before confirming import, assert none of the planned FinalNames collide
for _, name := range plannedFinalNames {
if filelock.IsExist(filepath.Join(util.DataDir, "assets", name)) {
return fmt.Errorf("assets/%s already exists; remove or rename it first", name)
}
} Type guard
func isIncompleteNotebookWrite(err error) bool {
var ue *obsidianUserError
return errors.As(err, &ue) && ue.DetailLanguage == 350
} Try / catch
// Not retryable as-is: clean the colliding entries under data/assets, // remove the incomplete notebook, then restart the vault import.
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/63b488d7f2491452.
Report an issue: GitHub.