siyuan-note/siyuan · error

AI editor action must not be empty

Error message

AI editor action must not be empty

What it means

SaveAIEditorAction validates that the supplied action carries at least a name or an action prompt. A nil pointer, or an object with both Name and Action empty, has nothing to persist and is rejected up-front.

Source

Thrown at kernel/model/ai_editor.go:72

func GetAIEditorActions() (ret []*AIEditorAction, err error) {
	waitForSyncingStorages()
	aiEditorActionsLock.Lock()
	defer aiEditorActionsLock.Unlock()

	data, err := loadAIEditorActions()
	if err != nil {
		return nil, err
	}
	if err = migrateLegacyAIEditorActions(data, !util.ReadOnly); err != nil {
		return nil, err
	}
	return data.Actions, nil
}

func SaveAIEditorAction(action *AIEditorAction) (ret *AIEditorAction, err error) {
	if action == nil || (action.Name == "" && action.Action == "") {
		return nil, errors.New("AI editor action must not be empty")
	}
	if action.ID != "" && !ast.IsNodeIDPattern(action.ID) {
		return nil, errors.New("invalid AI editor action ID")
	}

	waitForSyncingStorages()
	aiEditorActionsLock.Lock()
	defer aiEditorActionsLock.Unlock()

	data, err := loadAIEditorActions()
	if err != nil {
		return nil, err
	}
	if err = migrateLegacyAIEditorActions(data, true); err != nil {
		return nil, err
	}

	ret = &AIEditorAction{

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Ensure the request body includes a non-empty name or action prompt
  2. Return a validation error in the client before calling the API when both fields are blank
  3. Check the frontend form is binding inputs to the payload correctly

Example fix

// before
await fetchPost('/api/ai/saveAIEditorAction', {})
// after
await fetchPost('/api/ai/saveAIEditorAction', {name: 'Translate', action: 'Translate the selection to English'})
Defensive patterns

Strategy: validation

Validate before calling

if (!payload || (!payload.name && !payload.action)) { throw new Error('Name or action prompt is required') }

Type guard

const isValidActionInput = (a) => a != null && (typeof a.name === 'string' && a.name.trim() !== '' || typeof a.action === 'string' && a.action.trim() !== '')

Try / catch

try { await saveAction(a) } catch (e) { if (e.message.includes('must not be empty')) showToast('Fill in name or prompt') }

Prevention

When it happens

Trigger: Calling SaveAIEditorAction(nil) or SaveAIEditorAction(&AIEditorAction{}) (both fields empty) via the saveAIEditorAction API or tests.

Common situations: Frontend form submitted without filling fields; API call with an empty JSON body deserialized to a zero-value struct; client bug sending null payload.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/c8516b070ebbce4b. Report an issue: GitHub.