{"record":{"id":"28fcf38c71f5ba77","repo":"siyuan-note/siyuan","slug":"invalid-ai-editor-action-data","errorCode":null,"errorMessage":"invalid AI editor action data","messagePattern":"invalid AI editor action data","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/ai_editor.go","lineNumber":175,"sourceCode":"\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}\n\treturn ret, nil\n}\n\nfunc saveAIEditorActions(data *aiEditorActionsData) (err error) {\n\tdata.Version = aiEditorActionsVersion\n\tif data.Actions == nil {\n\t\tdata.Actions = []*AIEditorAction{}\n\t}\n\n\tdirPath := filepath.Dir(aiEditorActionsPath())\n\tif err = os.MkdirAll(dirPath, 0755); err != nil {\n\t\treturn fmt.Errorf(\"create AI editor actions directory failed: %w\", err)","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/ai_editor.go#L157-L193","documentation":"After parsing, loadAIEditorActions (kernel/model/ai_editor.go:175) sanity-checks every entry: it must be non-nil, its ID must match the node-ID pattern, and it must have a name or an action prompt. Any violation returns this generic data error — the file parses as JSON and has the right version, but its contents were not produced by the kernel.","triggerScenarios":"actions.json entries like null in the actions array, {\"name\":\"x\"} with no id, an id like \"foo\", or an entry with both name and action empty. Typically follows a hand edit or a bad merge from a sync tool rather than kernel writes, which always assign valid IDs.","commonSituations":"Hand-crafted action files missing the id field (the server always writes one); a JSON merge that dropped fields; copy-pasting an action between files without the id; nulls introduced by script-generated files.","solutions":["Open actions.json and fix each entry to the kernel shape: {\"id\":\"<14-digit-time>-<7-char-suffix>\",\"name\":\"...\",\"action\":\"...\"} with at least one of name/action non-empty","Give hand-added entries a correctly shaped unique ID, e.g. timestamp-based 20240101120000-a1b2c3d that no other entry uses","Or delete/rename the file — defaults are regenerated and legacy actions re-imported from localStorage"],"exampleFix":"// before: {\"version\":1,\"actions\":[{\"name\":\"Translate\"}]}\n// after:  {\"version\":1,\"actions\":[{\"id\":\"20240101120000-a1b2c3d\",\"name\":\"Translate\",\"action\":\"Translate to English:\\n{content}\"}]}","handlingStrategy":"try-catch","validationCode":"var nodeIDPattern = regexp.MustCompile(`^[0-9]{14}-[a-z0-9]{7}$`)\n\nfunc validEntries(actions []*AIEditorAction) bool {\n    for _, a := range actions {\n        if a == nil || !nodeIDPattern.MatchString(a.ID) || (a.Name == \"\" && a.Action == \"\") {\n            return false\n        }\n    }\n    return true\n}","typeGuard":"function isWellFormedAction(a: any): boolean {\n    return (\n        !!a &&\n        typeof a.id === \"string\" && /^[0-9]{14}-[a-z0-9]{7}$/.test(a.id) &&\n        (typeof a.name === \"string\" || typeof a.action === \"string\") &&\n        ((a.name ?? \"\") !== \"\" || (a.action ?? \"\") !== \"\")\n    );\n}","tryCatchPattern":"if err != nil && err.Error() == \"invalid AI editor action data\" {\n    // inspect actions.json entries one by one; fix shape or reset the file\n    backupAndResetActionsFile()\n    return\n}","preventionTips":["Only let the kernel write actions.json; script edits must mimic the exact shape including valid IDs","Validate imported action lists entry-by-entry before writing them","Prefer delete-and-recreate over surgical file edits when repairing"],"tags":["ai","data-corruption","json","validation"],"backgroundTag":"corrupt-config-file","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}