siyuan-note/siyuan · info
Conf.Language(331)
Error message
Conf.Language(331)
What it means
CancelObsidianVaultTask found the task but its State is not in a cancellable set (isObsidianCancellableState returned false), e.g. the import already completed, failed, or was cancelled. The kernel returns a snapshot of the task along with i18n message 331: 'The Obsidian Vault import task can no longer be cancelled'. This is a state-machine guard, not a data loss situation.
Source
Thrown at kernel/model/import_obsidian.go:295
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()
}
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))
}View on GitHub (pinned to 8641553a1f)
Solutions
- Check the task snapshot returned with the error: if the task already finished, the state is final and no cancel is needed
- Call GetObsidianVaultTask first and only cancel while the state is pending/analyzing/importing
- Treat the error as benign in the UI (show 'already finished') instead of surfacing it as a failure
- Avoid issuing cancel after observing a terminal state via polling
Example fix
// before
cancelObsidianTask(taskID) // errors if already finished
// after
task, _ := getObsidianVaultTask(taskID)
if isCancellableState(task.State) {
cancelObsidianTask(taskID)
} Defensive patterns
Strategy: type-guard
Validate before calling
task, err := GetObsidianVaultTask(taskID)
if err == nil && isCancellableState(task.State) { CancelObsidianVaultTask(taskID) } Type guard
func cancellable(t *ObsidianVaultTask) bool {
switch t.State {
case "pending", "analyzing", "importing":
return true
}
return false
} Try / catch
ret, err := CancelObsidianVaultTask(taskID)
if err != nil {
// ret still carries the final snapshot; treat as benign
log.Printf("task already finished: %v", err)
} Prevention
- Check current task state before issuing cancel
- Disable the cancel button in the UI once the state is terminal
- Expect the cancel API to return the task snapshot even when it errors
When it happens
Trigger: Calling CancelObsidianVaultTask(taskID) when the task.State is completed/failed/cancelled (any state outside isObsidianCancellableState) at kernel/model/import_obsidian.go:295.
Common situations: User double-clicks cancel; the import finished between listing the task and issuing the cancel; racing an auto-expiry that already finalized the task; automations issuing cancel without checking current state first.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Conf.Language(332)
- 349
- attribute view [%s] changed while loading search info
- read history snapshot [%s] failed: %w
- notebook [%s] not found for document [%s]
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/773afa2d70305ac4.
Report an issue: GitHub.