{"record":{"id":"645242234a9ffee5","repo":"siyuan-note/siyuan","slug":"the-obsidian-vault-analysis-is-not-ready-or-has-ex","errorCode":null,"errorMessage":"The Obsidian Vault analysis is not ready or has expired","messagePattern":"The Obsidian Vault analysis is not ready or has expired","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/import_obsidian.go","lineNumber":312,"sourceCode":"\t\tobsidianTasksMu.Unlock()\n\t\treturn ret, errors.New(Conf.Language(331))\n\t}\n\tif task.Cancel != nil {\n\t\ttask.Cancel()\n\t}\n\tfinishObsidianTaskLocked(task, ObsidianTaskStateCancelled, \"Import cancelled\", \"\")\n\tret := snapshotObsidianTask(task)\n\tobsidianTasksMu.Unlock()\n\tremoveObsidianTemp(taskID)\n\treturn ret, nil\n}\n\nfunc StartObsidianVaultImport(taskID, notebookName string) (*ObsidianVaultTask, error) {\n\tobsidianTasksMu.Lock()\n\ttask := obsidianTasks[taskID]\n\tif task == nil || task.State != ObsidianTaskStateReady || task.Context == nil {\n\t\tobsidianTasksMu.Unlock()\n\t\treturn nil, errors.New(Conf.Language(332))\n\t}\n\tif time.Now().After(task.ExpiresAt) {\n\t\tfinishObsidianTaskLocked(task, ObsidianTaskStateCancelled, \"Analysis expired\", \"\")\n\t\tobsidianTasksMu.Unlock()\n\t\treturn nil, errors.New(Conf.Language(332))\n\t}\n\n\tname := strings.TrimSpace(util.RemoveInvalid(notebookName))\n\tif name == \"\" {\n\t\tname = task.Analysis.NotebookName\n\t}\n\tctx, cancel := context.WithCancel(context.Background())\n\ttask.Cancel = cancel\n\ttask.State = ObsidianTaskStateRevalidating\n\ttask.Progress = 1\n\ttask.Message = \"Revalidating source files\"\n\tret := snapshotObsidianTask(task)\n\tobsidianTasksMu.Unlock()","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/import_obsidian.go#L294-L330","documentation":"Returned by StartObsidianVaultImport (i18n key 332) when the task does not exist, is not in the 'ready' state, or has a nil Context (the analysis result was not attached). This guards the import-start endpoint: only a completed analysis (state == ready) with a populated context can be promoted to the import phase. Any other state — queued, analyzing, failed, cancelled, or already-importing — is rejected.","triggerScenarios":"POST /api/import/startObsidianVaultImport with a taskID whose task is nil, whose State != ObsidianTaskStateReady, or whose Context == nil (line 310-312). Common when calling import before analysis finished, after analysis failed, or with a stale/reaped taskID.","commonSituations":"Calling import before polling confirms state == 'ready'; the analysis failed (state == failed) but the UI proceeds to import; the taskID is from a previous session/restart; the analysis was cancelled and the UI did not reset.","solutions":["Poll GET /api/import/getObsidianVaultTask until state == 'ready' before calling startObsidianVaultImport.","If the analysis failed or was cancelled, start a new analysis with startObsidianVaultAnalysis and use the new taskID.","Verify the taskID is the one returned by the most recent successful analysis.","Handle a 'failed' analysis state by showing the error detail rather than attempting import."],"exampleFix":"// before: calling import while still analyzing\npost(\"/api/import/startObsidianVaultImport\", {taskID, notebookName})\n// -> { code: -1, msg: \"... not ready or has expired\" }\n\n// after: wait for ready\ndo {\n    task = getObsidianVaultTask(taskID)\n} while (task && task.state === 'analyzing')\nif (task && task.state === 'ready') {\n    startObsidianVaultImport(taskID, notebookName)\n}","handlingStrategy":"validation","validationCode":"// Confirm the task is ready before calling startObsidianVaultImport\nfunc isReady(taskID string) bool {\n    model.obsidianTasksMu.Lock()\n    defer model.obsidianTasksMu.Unlock()\n    t := model.obsidianTasks[taskID]\n    return t != nil && t.State == \"ready\" && t.Context != nil\n}","typeGuard":"func isReadyTask(task *ObsidianVaultTask) bool {\n    return task != nil && task.State == \"ready\"\n}","tryCatchPattern":"task, err := model.StartObsidianVaultImport(taskID, notebookName)\nif err != nil && err.Error() == Conf.Language(332) {\n    // not ready — re-analyze or wait\n    return fmt.Errorf(\"analysis not ready; ensure state is 'ready' before importing\")\n}","preventionTips":["Poll getObsidianVaultTask until state == 'ready' before calling import.","Handle 'failed'/'cancelled' analysis states by restarting analysis.","Use the taskID from the most recent successful analysis only."],"tags":["obsidian","import","task-lifecycle","state-machine"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}