siyuan-note/siyuan · error
unmarshal legacy AI editor actions failed: %w
Error message
unmarshal legacy AI editor actions failed: %w
What it means
During legacy migration, the localStorage key "local-ai" exists and holds a STRING, but that string is not valid JSON for the expected shape []*legacyAIEditorAction{Name,Memo}. gulu.JSON.UnmarshalJSON returned a syntax or type error, so migration aborts and every Get/Save/Remove AI editor action call fails until the value is fixed.
Source
Thrown at kernel/model/ai_editor.go:215
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)
if marshalErr != nil {
return fmt.Errorf("marshal legacy AI editor actions failed: %w", marshalErr)
}
if err = gulu.JSON.UnmarshalJSON(legacyJSON, &legacyActions); err != nil {
return fmt.Errorf("unmarshal legacy AI editor actions failed: %w", err)
}
}
type actionKey struct {
name string
action string
}
existing := make(map[actionKey]struct{}, len(data.Actions))
for _, action := range data.Actions {
existing[actionKey{name: action.Name, action: action.Action}] = struct{}{}View on GitHub (pinned to afa823b6b4)
Solutions
- Inspect data/storage/local.json and validate the value of key "local-ai" with any JSON parser
- If it holds a string, decode/escape it and confirm it parses as [{"name":...,"memo":...}]
- Repair the JSON in place, or delete the local-ai key (old custom AI actions are discarded; the migration then no-ops)
- Back up local.json before editing, with SiYuan closed
Example fix
// before: corrupted value in data/storage/local.json
"local-ai": "[{name: 'translate', memo: 'Translate'}]" // single quotes, unquoted keys
// after: strict JSON, memo field
"local-ai": "[{\"name\":\"translate\",\"memo\":\"Translate the current block\"}]" Defensive patterns
Strategy: validation
Validate before calling
// Validate the legacy value before any AI editor action call triggers migration
ls := model.GetLocalStorage()
if raw, ok := ls["local-ai"]; ok {
if s, isStr := raw.(string); isStr {
var probe []*struct{ Name, Memo string }
if json.Unmarshal([]byte(s), &probe) != nil {
// repair or drop the key before proceeding
}
}
} Type guard
func isMigratableLegacyActions(raw any) bool {
s, ok := raw.(string)
if !ok { return false }
var probe []struct {
Name string `json:"name"`
Memo string `json:"memo"`
}
return json.Unmarshal([]byte(s), &probe) == nil
} Try / catch
if _, err := model.GetAIEditorActions(); err != nil && strings.Contains(err.Error(), "legacy AI editor actions") {
// stop calling in a loop; fix data/storage/local.json "local-ai" first, then retry once
} Prevention
- Never hand-edit storage/local.json while the kernel is running
- Validate local-ai JSON after restoring workspace from backup/sync
- Back up local.json before SiYuan upgrades that include AI editor migration
When it happens
Trigger: A pre-migration frontend stored custom AI actions as a string under local-ai in data/storage/local.json, and that string is truncated, hand-edited, corrupted by sync merge conflicts, or contains a different JSON shape (e.g. an object instead of an array of {name,memo}).
Common situations: Manual editing of storage/local.json, sync-tool merge conflicts producing half-written JSON, older SiYuan versions writing an incompatible local-ai schema, or crash during a previous localStorage write.
Related errors
- marshal legacy AI editor actions failed: %w
- remove legacy AI editor actions failed: %w
- attribute view spec is too new
- parse json [%s] to tree failed: %w
- unmarshal AI editor actions failed: %w
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/cae76dfdae294da5.
Report an issue: GitHub.