siyuan-note/siyuan · warning

The Obsidian Vault analysis is not ready or has expired

Error message

The Obsidian Vault analysis is not ready or has expired

What it means

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.

Source

Thrown at kernel/model/import_obsidian.go:312

		obsidianTasksMu.Unlock()
		return ret, errors.New(Conf.Language(331))
	}
	if task.Cancel != nil {
		task.Cancel()
	}
	finishObsidianTaskLocked(task, ObsidianTaskStateCancelled, "Import cancelled", "")
	ret := snapshotObsidianTask(task)
	obsidianTasksMu.Unlock()
	removeObsidianTemp(taskID)
	return ret, nil
}

func StartObsidianVaultImport(taskID, notebookName string) (*ObsidianVaultTask, error) {
	obsidianTasksMu.Lock()
	task := obsidianTasks[taskID]
	if task == nil || task.State != ObsidianTaskStateReady || task.Context == nil {
		obsidianTasksMu.Unlock()
		return nil, errors.New(Conf.Language(332))
	}
	if time.Now().After(task.ExpiresAt) {
		finishObsidianTaskLocked(task, ObsidianTaskStateCancelled, "Analysis expired", "")
		obsidianTasksMu.Unlock()
		return nil, errors.New(Conf.Language(332))
	}

	name := strings.TrimSpace(util.RemoveInvalid(notebookName))
	if name == "" {
		name = task.Analysis.NotebookName
	}
	ctx, cancel := context.WithCancel(context.Background())
	task.Cancel = cancel
	task.State = ObsidianTaskStateRevalidating
	task.Progress = 1
	task.Message = "Revalidating source files"
	ret := snapshotObsidianTask(task)
	obsidianTasksMu.Unlock()

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Poll GET /api/import/getObsidianVaultTask until state == 'ready' before calling startObsidianVaultImport.
  2. If the analysis failed or was cancelled, start a new analysis with startObsidianVaultAnalysis and use the new taskID.
  3. Verify the taskID is the one returned by the most recent successful analysis.
  4. Handle a 'failed' analysis state by showing the error detail rather than attempting import.

Example fix

// before: calling import while still analyzing
post("/api/import/startObsidianVaultImport", {taskID, notebookName})
// -> { code: -1, msg: "... not ready or has expired" }

// after: wait for ready
do {
    task = getObsidianVaultTask(taskID)
} while (task && task.state === 'analyzing')
if (task && task.state === 'ready') {
    startObsidianVaultImport(taskID, notebookName)
}
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the task is ready before calling startObsidianVaultImport
func isReady(taskID string) bool {
    model.obsidianTasksMu.Lock()
    defer model.obsidianTasksMu.Unlock()
    t := model.obsidianTasks[taskID]
    return t != nil && t.State == "ready" && t.Context != nil
}

Type guard

func isReadyTask(task *ObsidianVaultTask) bool {
    return task != nil && task.State == "ready"
}

Try / catch

task, err := model.StartObsidianVaultImport(taskID, notebookName)
if err != nil && err.Error() == Conf.Language(332) {
    // not ready — re-analyze or wait
    return fmt.Errorf("analysis not ready; ensure state is 'ready' before importing")
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/645242234a9ffee5. Report an issue: GitHub.