siyuan-note/siyuan · error

marshal AI editor actions failed: %w

Error message

marshal AI editor actions failed: %w

What it means

saveAIEditorActions marshals the aiEditorActionsData struct to indented JSON with gulu.JSON.MarshalIndentJSON before writing. This error wraps any failure from that marshal step. In practice Go's encoding/json only fails for values it cannot represent (e.g. unsupported types like channels or funcs reachable through the struct), so this is rare and points to an internal invariant rather than user input.

Source

Thrown at kernel/model/ai_editor.go:197

		}
		ids[action.ID] = struct{}{}
	}
	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)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Read the wrapped error to identify the offending field/type reported by encoding/json
  2. Ensure every field in aiEditorActionsData/AIEditorAction is of JSON-encodable types (string, int, slices, pointers)
  3. Verify no third-party patch changed the struct; revert to upstream struct definitions
  4. If caused by in-memory corruption, restart the kernel so the store is reloaded from disk

Example fix

// before: a patched struct with an unsupported field
type AIEditorAction struct {
    ID     string `json:"id"`
    Hook   func() `json:"hook"` // not JSON-encodable
}
// after: keep only encodable fields
type AIEditorAction struct {
    ID     string `json:"id"`
    Name   string `json:"name"`
    Action string `json:"action"`
}
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity-check the in-memory value encodes before persisting
if _, err := json.Marshal(data); err != nil {
    return fmt.Errorf("in-memory actions not encodable: %w", err)
}

Try / catch

if err := SaveAIEditorAction(act); err != nil && strings.Contains(err.Error(), "marshal AI editor actions failed") {
    return fmt.Errorf("internal encoding failure, restart the kernel: %w", err)
}

Prevention

When it happens

Trigger: SaveAIEditorAction, RemoveAIEditorAction, or migrateLegacyAIEditorActions calls saveAIEditorActions with a data value whose marshaling fails — essentially only if AIEditorAction/aiEditorActionsData gained a field of an unsupported type, or a corrupted in-memory action value cannot be encoded.

Common situations: A custom kernel build or patched fork added a field to AIEditorAction with an unsupported type (chan, func, complex); a nil-bearing data structure from a broken caller path defeats encoding assumptions.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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