{"record":{"id":"ec10af8557645e5b","repo":"siyuan-note/siyuan","slug":"unmarshal-ai-editor-actions-failed-w","errorCode":null,"errorMessage":"unmarshal AI editor actions failed: %w","messagePattern":"unmarshal AI editor actions failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/ai_editor.go","lineNumber":163,"sourceCode":"\treturn filepath.Join(util.DataDir, \"storage\", \"ai\", \"editor\", \"actions.json\")\n}\n\nfunc loadAIEditorActions() (ret *aiEditorActionsData, err error) {\n\tret = &aiEditorActionsData{\n\t\tVersion: aiEditorActionsVersion,\n\t\tActions: []*AIEditorAction{},\n\t}\n\tdataPath := aiEditorActionsPath()\n\tif !filelock.IsExist(dataPath) {\n\t\treturn ret, nil\n\t}\n\n\tdata, err := filelock.ReadFile(dataPath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read AI editor actions failed: %w\", err)\n\t}\n\tif err = gulu.JSON.UnmarshalJSON(data, ret); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshal AI editor actions failed: %w\", err)\n\t}\n\tif ret.Version != aiEditorActionsVersion {\n\t\treturn nil, fmt.Errorf(\"unsupported AI editor actions version [%d]\", ret.Version)\n\t}\n\tif ret.Actions == nil {\n\t\tret.Actions = []*AIEditorAction{}\n\t}\n\n\tids := make(map[string]struct{}, len(ret.Actions))\n\tfor _, action := range ret.Actions {\n\t\tif action == nil || !ast.IsNodeIDPattern(action.ID) || (action.Name == \"\" && action.Action == \"\") {\n\t\t\treturn nil, errors.New(\"invalid AI editor action data\")\n\t\t}\n\t\tif _, ok := ids[action.ID]; ok {\n\t\t\treturn nil, fmt.Errorf(\"duplicate AI editor action ID [%s]\", action.ID)\n\t\t}\n\t\tids[action.ID] = struct{}{}\n\t}","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/ai_editor.go#L145-L181","documentation":"loadAIEditorActions (kernel/model/ai_editor.go:163) unmarshals the contents of <workspace>/data/storage/ai/editor/actions.json with gulu.JSON.UnmarshalJSON. If the bytes are not valid JSON, the parse error is wrapped with this message. The file is machine-written, so corruption almost always means an external edit or an interrupted write.","triggerScenarios":"actions.json was hand-edited and now has a trailing comma, unquoted key, or was truncated; a crash or power loss left a partial file; the file was replaced by merge-conflict output from a sync tool (<<<<<<< markers); encoding damage (BOM/cut multi-byte char).","commonSituations":"Users hand-tuning actions in a text editor; two SiYuan instances or a sync tool (WebDAV/cloud drive) racing on the same workspace; file truncated by a full disk during save.","solutions":["Open <workspace>/data/storage/ai/editor/actions.json in an editor and run a JSON validator; fix the syntax error keeping the {\"version\":1,\"actions\":[...]} shape","If unfixable, rename/delete the file — the kernel recreates defaults and migrates legacy actions from the localStorage key \"local-ai\"","Prevent recurrence: never edit while SiYuan is running, and avoid two instances sharing one workspace","Restore from a workspace backup/snapshot if the actions are precious"],"exampleFix":"// before: actions.json contains {\"version\":1,\"actions\":[{\"id\":\"20240101120000-a1b2c3d\",\"name\":\"Translate\",]}\n// after:  {\"version\":1,\"actions\":[{\"id\":\"20240101120000-a1b2c3d\",\"name\":\"Translate\"}]}","handlingStrategy":"try-catch","validationCode":"data, err := os.ReadFile(path)\nif err == nil {\n    if json.Valid(data) {\n        _ = json.Unmarshal(data, &aiEditorActionsData{}) // dry-run parse\n    } else {\n        return errors.New(\"actions.json is corrupt; restore or delete it\")\n    }\n}","typeGuard":null,"tryCatchPattern":"actions, err := model.GetAIEditorActions()\nif err != nil && strings.HasPrefix(err.Error(), \"unmarshal AI editor actions failed\") {\n    backupAndResetActionsFile() // rename file; kernel regenerates + re-imports legacy 'local-ai'\n    actions, err = model.GetAIEditorActions()\n}","preventionTips":["Never hand-edit actions.json while the kernel is running","Run one SiYuan instance per workspace; concurrent sync tools corrupt JSON stores","Keep the disk from filling — truncated writes are a classic corruption source"],"tags":["ai","json","data-corruption","filesystem"],"backgroundTag":"json-parse-error","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}