siyuan-note/siyuan · warning

The Obsidian Vault import task was not found or has expired

Error message

The Obsidian Vault import task was not found or has expired

What it means

Returned by GetObsidianVaultTask (i18n key 330) when no task exists in the in-memory obsidianTasks map for the given taskID. Tasks are deleted after obsidianResultTTL (10 minutes) following a terminal state, and analysis tasks are deleted after obsidianReadyTTL expiry. A taskID that never existed or has been reaped produces this error.

Source

Thrown at kernel/model/import_obsidian.go:280

	task := &obsidianTask{TaskID: taskID, State: ObsidianTaskStateQueued, Message: "Waiting to analyze", Cancel: cancel}
	obsidianTasks[taskID] = task
	obsidianActive = taskID
	ret := snapshotObsidianTask(task)
	obsidianTasksMu.Unlock()

	if replacedTaskID != "" {
		removeObsidianTemp(replacedTaskID)
	}
	go analyzeObsidianVaultTask(ctx, taskID, localPath)
	return ret, nil
}

func GetObsidianVaultTask(taskID string) (*ObsidianVaultTask, error) {
	obsidianTasksMu.Lock()
	defer obsidianTasksMu.Unlock()
	task := obsidianTasks[taskID]
	if task == nil {
		return nil, errors.New(Conf.Language(330))
	}
	return snapshotObsidianTask(task), nil
}

func CancelObsidianVaultTask(taskID string) (*ObsidianVaultTask, error) {
	obsidianTasksMu.Lock()
	task := obsidianTasks[taskID]
	if task == nil {
		obsidianTasksMu.Unlock()
		return nil, errors.New(Conf.Language(330))
	}
	if !isObsidianCancellableState(task.State) {
		ret := snapshotObsidianTask(task)
		obsidianTasksMu.Unlock()
		return ret, errors.New(Conf.Language(331))
	}
	if task.Cancel != nil {
		task.Cancel()

View on GitHub (pinned to 251596fc0d)

Solutions

  1. If the task was reaped after completion, start a fresh analysis with startObsidianVaultAnalysis.
  2. Verify the taskID matches the value returned by the original startObsidianVaultAnalysis or startObsidianVaultImport call.
  3. Poll more frequently than the 10-minute result TTL to avoid missing the terminal state.
  4. Handle this error in the UI by resetting to the import-entry screen rather than looping.

Example fix

// before
resp, _ := post("/api/import/getObsidianVaultTask", {taskID: staleID})
// -> { code: -1, msg: "The Obsidian Vault import task was not found or has expired" }

// after: treat not-found/expired as 'start over'
if resp.code !== 0 {
    showImportEntryScreen() // user re-selects vault and starts fresh
}
Defensive patterns

Strategy: try-catch

Try / catch

task, err := model.GetObsidianVaultTask(taskID)
if err != nil {
    if err.Error() == Conf.Language(330) {
        // task reaped or unknown — reset to import entry, do not loop
        return nil, ErrTaskGone
    }
    return nil, err
}

Prevention

When it happens

Trigger: POST /api/import/getObsidianVaultTask with a taskID that is nil/empty in the map (line 278-280). Occurs when polling a task more than 10 minutes after it finished, polling a fabricated/typo'd ID, or polling after a server restart cleared the in-memory map.

Common situations: The UI retains a stale taskID across a page reload or kernel restart; more than 10 minutes elapsed since the task completed; the taskID was mistyped or truncated in transit; the analysis expired and was reaped.

Related errors


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