siyuan-note/siyuan · error

write AI editor actions failed: %w

Error message

write AI editor actions failed: %w

What it means

saveAIEditorActions writes the marshaled JSON via filelock.WriteFile to data/storage/ai/editor/actions.json; a failure there is wrapped with this message. The directory was created successfully, so the failure is at the file level: permissions, disk full, lock contention, or an I/O error while replacing the file.

Source

Thrown at kernel/model/ai_editor.go:200

	return ret, nil
}

func saveAIEditorActions(data *aiEditorActionsData) (err error) {
	data.Version = aiEditorActionsVersion
	if data.Actions == nil {
		data.Actions = []*AIEditorAction{}
	}

	dirPath := filepath.Dir(aiEditorActionsPath())
	if err = os.MkdirAll(dirPath, 0755); err != nil {
		return fmt.Errorf("create AI editor actions directory failed: %w", err)
	}
	bytes, err := gulu.JSON.MarshalIndentJSON(data, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal AI editor actions failed: %w", err)
	}
	if err = filelock.WriteFile(aiEditorActionsPath(), bytes); err != nil {
		return fmt.Errorf("write AI editor actions failed: %w", err)
	}
	return nil
}

func migrateLegacyAIEditorActions(data *aiEditorActionsData, persist bool) (err error) {
	localStorage := GetLocalStorage()
	legacyRaw, ok := localStorage[legacyAIEditorActionsStorageKey]
	if !ok {
		return nil
	}

	var legacyActions []*legacyAIEditorAction
	if legacyJSON, isString := legacyRaw.(string); isString {
		if err = gulu.JSON.UnmarshalJSON([]byte(legacyJSON), &legacyActions); err != nil {
			return fmt.Errorf("unmarshal legacy AI editor actions failed: %w", err)
		}
	} else {
		legacyJSON, marshalErr := gulu.JSON.MarshalJSON(legacyRaw)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Check the wrapped OS error and act accordingly: ENOSPC → free space; EACCES → fix permissions/ownership on data/storage/ai/editor/actions.json
  2. Ensure only one SiYuan instance uses this workspace and exclude the data directory from backup/AV file locking
  3. Verify the storage volume is mounted, writable, and not read-only
  4. Retry the save after transient conditions clear; if the file is locked persistently, find and close the holder (lsof/handle.exe)

Example fix

// before: file owned by root, kernel runs as user
-rw-r--r-- 1 root root actions.json
// after
sudo chown siyuan:siyuan actions.json && sudo chmod 644 actions.json
Defensive patterns

Strategy: retry

Validate before calling

// ensure target file is writable before saving
p := "data/storage/ai/editor/actions.json"
if f, err := os.OpenFile(p, os.O_WRONLY, 0644); err != nil {
    return fmt.Errorf("actions.json not writable: %w", err)
} else { f.Close() }

Try / catch

err := SaveAIEditorAction(act)
if err != nil && strings.Contains(err.Error(), "write AI editor actions failed") {
    time.Sleep(200 * time.Millisecond)
    err = SaveAIEditorAction(act) // retry once for transient lock/IO issues
}

Prevention

When it happens

Trigger: SaveAIEditorAction, RemoveAIEditorAction, or migrateLegacyAIEditorActions persists the store and filelock.WriteFile fails: actions.json exists but is not writable, the lock file cannot be acquired, the volume is full or read-only, or an EIO occurs mid-write.

Common situations: Disk quota or space exhausted on the workspace volume; the file is owned by another user (e.g. created earlier under sudo/root); a second SiYuan instance or backup tool holds the lock; the workspace lives on a removable/network drive that dropped mid-write; antivirus interference on Windows.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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