siyuan-note/siyuan · error

read AI editor actions failed: %w

Error message

read AI editor actions failed: %w

What it means

loadAIEditorActions (kernel/model/ai_editor.go:160) reads the persisted actions file at <workspace>/data/storage/ai/editor/actions.json through filelock.ReadFile. If the OS-level read fails (the file exists per filelock.IsExist but cannot be read), the raw error is wrapped with this message and every AI-editor-action API call aborts. This is an environment problem, not a data-format problem.

Source

Thrown at kernel/model/ai_editor.go:160

}

func aiEditorActionsPath() string {
	return filepath.Join(util.DataDir, "storage", "ai", "editor", "actions.json")
}

func loadAIEditorActions() (ret *aiEditorActionsData, err error) {
	ret = &aiEditorActionsData{
		Version: aiEditorActionsVersion,
		Actions: []*AIEditorAction{},
	}
	dataPath := aiEditorActionsPath()
	if !filelock.IsExist(dataPath) {
		return ret, nil
	}

	data, err := filelock.ReadFile(dataPath)
	if err != nil {
		return nil, fmt.Errorf("read AI editor actions failed: %w", err)
	}
	if err = gulu.JSON.UnmarshalJSON(data, ret); err != nil {
		return nil, fmt.Errorf("unmarshal AI editor actions failed: %w", err)
	}
	if ret.Version != aiEditorActionsVersion {
		return nil, fmt.Errorf("unsupported AI editor actions version [%d]", ret.Version)
	}
	if ret.Actions == nil {
		ret.Actions = []*AIEditorAction{}
	}

	ids := make(map[string]struct{}, len(ret.Actions))
	for _, action := range ret.Actions {
		if action == nil || !ast.IsNodeIDPattern(action.ID) || (action.Name == "" && action.Action == "") {
			return nil, errors.New("invalid AI editor action data")
		}
		if _, ok := ids[action.ID]; ok {
			return nil, fmt.Errorf("duplicate AI editor action ID [%s]", action.ID)

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Check permissions and ownership of <workspace>/data/storage/ai/editor/actions.json and its parent directories (chmod/chown so the SiYuan process can read)
  2. Verify the workspace filesystem is mounted read-write and healthy (dmesg/Event Viewer for disk errors)
  3. Close other processes holding the file (sync clients, editors, antivirus) and retry
  4. As a last resort, move actions.json aside — the kernel regenerates defaults and re-imports legacy actions from localStorage
Defensive patterns

Strategy: try-catch

Validate before calling

path := filepath.Join(util.DataDir, "storage", "ai", "editor", "actions.json")
if info, err := os.Stat(path); err == nil {
    if info.Mode().Perm()&0400 == 0 {
        return fmt.Errorf("actions file not readable; fix permissions on %s", path)
    }
}

Try / catch

actions, err := model.GetAIEditorActions()
if err != nil && strings.HasPrefix(err.Error(), "read AI editor actions failed") {
    // environment problem: check perms/mount/disk, then retry after fixing
    reportFileSystemIssue(err)
    return
}

Prevention

When it happens

Trigger: The actions.json file exists but read(2) fails: permission denied (file or directory mode/ownership), an unreadable network/mount filesystem, the file held by another process with exclusive access, or a disk I/O error surfaced by the kernel.

Common situations: Workspace moved between users so storage/ is owned by another UID; workspace on a failing or unmounted drive; antivirus/backup tools locking the file on Windows; read-only mount after an OS update.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/c31caf55e3693402. Report an issue: GitHub.