{"record":{"id":"ef0c0c814404ad19","repo":"siyuan-note/siyuan","slug":"conf-language-329","errorCode":null,"errorMessage":"Conf.Language(329)","messagePattern":"Conf\\.Language\\(329\\)","errorType":"exception","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/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/import_obsidian.go#L232-L268","documentation":"Only one Obsidian Vault import task may run at a time. `ImportObsidianVault` checks `obsidianActive`; if another task is active and already past its pre-import phase (i.e. actually importing), it returns the localized message 'An Obsidian Vault import task is already running' (Conf.Language(329)). This serializes the heavyweight vault conversion.","triggerScenarios":"Calling the Obsidian import API while another import task is in a non-terminal, non-pre-import state — e.g. double-clicking the import button, or a plugin/script starting a second import before the first finishes or is cancelled.","commonSituations":"User retries impatiently during a long vault conversion; two clients (desktop + API call) start imports concurrently; a previous task is hung in the importing state and blocks new ones.","solutions":["Poll `GetObsidianVaultTask(activeTaskID)` and wait until the running task reaches a terminal state (finished/cancelled/failed) before starting a new import","Call `CancelObsidianVaultTask(taskID)` on the active task, then start the new import","Avoid duplicate submissions in the UI (disable the button while a task is running)","If a task appears permanently stuck, restart the kernel to clear the in-memory task registry"],"exampleFix":"// before\nstartImport() // second call -> Conf.Language(329)\n// after\ntask, err := GetObsidianVaultTask(activeTaskID)\nif err == nil && !isObsidianTerminalState(task.State) {\n    CancelObsidianVaultTask(activeTaskID) // wait for cancellation\n}\nstartImport()","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const task = await importObsidianVault(p)\n} catch (e) {\n  if (e.code === 329) {\n    await waitForTerminalState(activeTaskID) // poll GetObsidianVaultTask\n    await importObsidianVault(p) // retry once the active task finished/cancelled\n  }\n}","preventionTips":["Track the active task ID and poll its state before offering the import action","Disable the import button in the UI while a task is in a non-terminal state","Cancel the running task explicitly before starting a new one","Never launch imports concurrently from scripts or multiple clients"],"tags":["obsidian","concurrency","task-management","import"],"backgroundTag":"invalid-state-transition","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}