babalae/better-genshin-impact · error · Exception

Failed to deserialize macro

Error message

Failed to deserialize macro

What it means

Thrown by KeyMouseMacroPlayer.PlayMacro at line 31 when JsonSerializer.Deserialize<KeyMouseScript>(macro, KeyMouseRecorder.JsonOptions) yields null. Note the `?? throw` only fires when deserialization succeeds but returns a null reference (input JSON is the literal "null"); malformed or empty JSON throws JsonException BEFORE reaching this line. KeyMouseRecorder.JsonOptions uses camelCase naming, allows comments and trailing commas, so structurally-permissive but schema-mismatched input still deserializes to a default/null object rather than throwing.

Source

Thrown at BetterGenshinImpact/Core/Recorder/KeyMouseMacroPlayer.cs:31

using System.Threading.Tasks;
using System.Windows.Forms;
using Fischless.WindowsInput;
using Vanara.PInvoke;
using Wpf.Ui.Violeta.Controls;

namespace BetterGenshinImpact.Core.Recorder;

public class KeyMouseMacroPlayer
{
    public static async Task PlayMacro(string macro, CancellationToken ct, bool withDelay = true)
    {
        if (!TaskContext.Instance().IsInitialized)
        {
            Toast.Warning("请先在启动页,启动截图器再使用本功能");
            return;
        }

        var script = JsonSerializer.Deserialize<KeyMouseScript>(macro, KeyMouseRecorder.JsonOptions) ?? throw new Exception("Failed to deserialize macro");
        script.Adapt(TaskContext.Instance().SystemInfo.CaptureAreaRect, TaskContext.Instance().DpiScale);
        SystemControl.ActivateWindow();

        if (withDelay)
        {
            for (var i = 3; i >= 1; i--)
            {
                TaskControl.Logger.LogInformation("{Sec}秒后进行重放...", i);
                await Task.Delay(1000, ct);
            }

            TaskControl.Logger.LogInformation("开始重放");
        }

        await PlayMacro(script.MacroEvents, ct);
    }

    public static async Task PlayMacro(List<MacroEvent> macroEvents, CancellationToken ct)

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Validate the macro string before calling PlayMacro: reject null, empty, whitespace, and the literal "null".
  2. Catch JsonException separately around the Deserialize call to surface real parse errors with line/byte position.
  3. If the macro file is corrupt, re-record it with the built-in KeyMouseRecorder and overwrite the file.
  4. Log the file path and first 200 chars of the macro string when this fires to identify which file is bad.

Example fix

// before
var script = JsonSerializer.Deserialize<KeyMouseScript>(macro, KeyMouseRecorder.JsonOptions)
    ?? throw new Exception("Failed to deserialize macro");

// after
if (string.IsNullOrWhiteSpace(macro) || macro.Trim() == "null")
{
    throw new InvalidOperationException("Macro content is empty or null; the macro file may be corrupt. Re-record it.");
}
KeyMouseScript script;
try
{
    script = JsonSerializer.Deserialize<KeyMouseScript>(macro, KeyMouseRecorder.JsonOptions)
        ?? throw new InvalidOperationException("Failed to deserialize macro: result was null. File may be corrupt or schema-mismatched.");
}
catch (JsonException ex)
{
    throw new InvalidOperationException($"Failed to parse macro JSON at {ex.Path}: {ex.Message}", ex);
}
Defensive patterns

Strategy: validation

Validate before calling

// C# caller guard before PlayMacro
if (string.IsNullOrWhiteSpace(macro) || macro.Trim() == "null")
{
    throw new InvalidOperationException("Macro content is empty/null; the macro file may be corrupt. Re-record it.");
}

Try / catch

try
{
    var script = JsonSerializer.Deserialize<KeyMouseScript>(macro, KeyMouseRecorder.JsonOptions);
    if (script is null) throw new InvalidOperationException("Macro deserialized to null; file may be corrupt.");
}
catch (JsonException ex)
{
    // Surface the real parse error (path/position) instead of the generic message
    logger.LogError(ex, "Macro JSON parse failed at {Path}", ex.Path);
    throw;
}

Prevention

When it happens

Trigger: Calling PlayMacro(macro, ct) where `macro` is the string "null", or where a recorded macro file is empty/contains only "null". Passing a non-KeyMouseScript-shaped JSON object whose root deserializes to null. Reading a corrupt or truncated .json macro file whose saved content became "null".

Common situations: A macro .json file was corrupted by an interrupted write (power loss, crash mid-save), leaving it empty or containing "null". The user hand-edited the JSON and removed the macroEvents/info fields. A schema/version change in KeyMouseScript made old files deserialize to null. Passing the wrong file's contents (e.g. a config file) into PlayMacro.

Related errors


AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13). Data as JSON: /api/errors/ebdc788f4755d7df. Report an issue: GitHub.