siyuan-note/siyuan · error
AI editor action not found
Error message
AI editor action not found
What it means
When SaveAIEditorAction is called with a non-empty ID, it treats the call as an update: it searches the stored actions for a matching ID and replaces it. If no stored action has that ID, `updated` stays false and SiYuan returns "AI editor action not found" — you cannot 'update' an action that was never created (or was deleted).
Solutions
- List current actions (GetAIEditorActions) and confirm the ID exists before updating
- If creating is intended, omit the ID so the kernel appends a new action
- Refresh the client's cached action list after deletes/sync to avoid stale IDs
- Handle the error by falling back to a create (save without ID) if appropriate
Example fix
// before
saveAIEditorAction(&AIEditorAction{ID: staleID, Name: "Renamed"})
// after
actions, _ := GetAIEditorActions()
if !containsID(actions, staleID) { return saveAIEditorAction(&AIEditorAction{Name: "Renamed"}) } // create instead Defensive patterns
Strategy: validation
Validate before calling
const actions = await fetchPost("/api/ai/editor/action/list", {});
if (!actions.some(a => a.id === id)) throw new Error("Action no longer exists; refresh the list"); Try / catch
try {
await saveAIEditorAction({ id, ...changes });
} catch (e) {
if (e.message.includes("not found")) {
// refresh action list; offer to recreate the action
}
} Prevention
- Refresh the cached action list before updates
- Re-list after deletes and after sync completes
- Distinguish create (no ID) from update (existing ID) in client code
When it happens
Trigger: Calling SaveAIEditorAction with a well-formed but nonexistent ID; updating an action that was removed in another session or another synced device; stale IDs cached by a plugin after the action list changed.
Common situations: Race between two clients where one deletes the action while another edits it; plugin holding a stale snapshot of the action list; typo'd ID that happens to match the node-ID pattern.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- AI editor action must not be empty
- Content block with id
- generate image failed
- get block failed:
- invalid provider config
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/b075aa5c8a7c0dac.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/ai_editor.go:107
ret = &AIEditorAction{
ID: action.ID,
Name: action.Name,
Action: action.Action,
}
if ret.ID == "" {
ret.ID = ast.NewNodeID()
data.Actions = append(data.Actions, ret)
} else {
updated := false
for i, item := range data.Actions {
if item.ID == ret.ID {
data.Actions[i] = ret
updated = true
break
}
}
if !updated {
return nil, errors.New("AI editor action not found")
}
}
if err = saveAIEditorActions(data); err != nil {
return nil, err
}
return ret, nil
}
func RemoveAIEditorAction(id string) (err error) {
if !ast.IsNodeIDPattern(id) {
return errors.New("invalid AI editor action ID")
}
waitForSyncingStorages()
aiEditorActionsLock.Lock()
defer aiEditorActionsLock.Unlock()View on GitHub (pinned to 9f775e8a12)