{"record":{"id":"ca6a7190264c15f3","repo":"siyuan-note/siyuan","slug":"an-obsidian-vault-import-task-is-already-running","errorCode":null,"errorMessage":"An Obsidian Vault import task is already running","messagePattern":"An Obsidian Vault import task is already running","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/import_obsidian.go","lineNumber":250,"sourceCode":"\terrObsidianVaultUnsafePath      = errors.New(\"Obsidian Vault path is unsafe\")\n\terrObsidianVaultConfigMissing   = errors.New(\"Obsidian Vault config directory is missing\")\n\terrObsidianVaultMarkdownMissing = errors.New(\"Obsidian Vault has no readable Markdown\")\n\terrObsidianSourceChanged        = errors.New(\"Obsidian source file changed\")\n\n\tobsidianBlockIDPattern  = regexp.MustCompile(`(?m)(?:^|[ \\t])\\^([A-Za-z0-9-]+)[ \\t]*$`)\n\tobsidianQuotePattern    = regexp.MustCompile(`^((?:[ \\t]*>[ \\t]?)+)(.*)$`)\n\tobsidianListItemPattern = regexp.MustCompile(`^([ \\t]*(?:[-+*]|\\d+[.)])[ \\t]+)(.*)$`)\n\tobsidianFootnotePattern = regexp.MustCompile(`(?m)\\[\\^[^\\]\\r\\n]+\\]`)\n)\n\nfunc StartObsidianVaultAnalysis(localPath string) (*ObsidianVaultTask, error) {\n\tvar replacedTaskID string\n\tobsidianTasksMu.Lock()\n\tif obsidianActive != \"\" {\n\t\tif active := obsidianTasks[obsidianActive]; active != nil && !isObsidianTerminalState(active.State) {\n\t\t\tif !isObsidianPreImportState(active.State) {\n\t\t\t\tobsidianTasksMu.Unlock()\n\t\t\t\treturn nil, errors.New(Conf.Language(329))\n\t\t\t}\n\t\t\tif active.Cancel != nil {\n\t\t\t\tactive.Cancel()\n\t\t\t}\n\t\t\treplacedTaskID = active.TaskID\n\t\t\tfinishObsidianTaskLocked(active, ObsidianTaskStateCancelled, \"Import cancelled\", \"\")\n\t\t}\n\t}\n\n\ttaskID := ast.NewNodeID()\n\tctx, cancel := context.WithCancel(context.Background())\n\ttask := &obsidianTask{TaskID: taskID, State: ObsidianTaskStateQueued, Message: \"Waiting to analyze\", Cancel: cancel}\n\tobsidianTasks[taskID] = task\n\tobsidianActive = taskID\n\tret := snapshotObsidianTask(task)\n\tobsidianTasksMu.Unlock()\n\n\tif replacedTaskID != \"\" {","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/import_obsidian.go#L232-L268","documentation":"Returned by StartObsidianVaultAnalysis (i18n key 329) when another Obsidian import task is already active and has progressed past the pre-import states (queued/analyzing/ready). Only one import can run at a time; tasks in creating/writing/indexing/revalidating/staging states cannot be displaced. A task still in a pre-import state is cancelled and replaced silently; this error fires only for tasks that have committed to the import phase.","triggerScenarios":"POST /api/import/startObsidianVaultAnalysis while a prior task is in a non-terminal, non-pre-import state (isObsidianTerminalState is false and isObsidianPreImportState is false — i.e. state is revalidating, staging, creating, writing, or indexing). The second analysis call is rejected.","commonSituations":"User clicks 'import' again while a first import is mid-flight in the UI; a stale task ID was not cleaned up and the user retries; a scripted client fires two analysis requests concurrently.","solutions":["Wait for the in-progress import task to reach a terminal state (completed/failed/cancelled) before starting a new analysis.","Call POST /api/import/cancelObsidianVaultTask with the active taskID to cancel it, then retry analysis.","Poll GET /api/import/getObsidianVaultTask until state is terminal, then proceed.","Ensure UI controls disable the import button while a task is active."],"exampleFix":"// before: concurrent analysis while import is writing\n// POST /api/import/startObsidianVaultAnalysis -> Language(329)\n\n// after: cancel or wait first\ntask, _ := getObsidianVaultTask(activeTaskID)\nif task != nil && !isTerminal(task.State) {\n    cancelObsidianVaultTask(activeTaskID)\n}\nstartObsidianVaultAnalysis(newLocalPath)","handlingStrategy":"validation","validationCode":"// Check no active import is running before starting analysis\nif model.IsObsidianVaultTaskActive() {\n    return errors.New(\"an Obsidian import task is already active\")\n}","typeGuard":null,"tryCatchPattern":"task, err := model.StartObsidianVaultAnalysis(localPath)\nif err != nil && err.Error() == Conf.Language(329) {\n    // cancel the active task or wait for it, then retry\n    return fmt.Errorf(\"an import is already running (task %s); cancel it first\", obsidianActive)\n}","preventionTips":["Disable the import button in the UI while a task is active.","Cancel or wait for the running task before initiating a new one.","Poll IsObsidianVaultTaskActive before calling startObsidianVaultAnalysis."],"tags":["obsidian","import","concurrency","task-lifecycle"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}