babalae/better-genshin-impact · error · ArgumentNullException

实时任务对象不能为空

Error message

实时任务对象不能为空

What it means

ArgumentNullException thrown by Dispatcher.AddTrigger at line 87 when the RealtimeTimer argument is null. Dispatcher is a ClearScript host object exposed to JS scripts; this guard fires when JS calls dispatcher.addTrigger(null)/undefined. The preceding `var realtimeTimer = timer` assignment is a no-op — the null check is what protects TaskTriggerDispatcher from receiving a null trigger.

Source

Thrown at BetterGenshinImpact/Core/Script/Dependence/Dispatcher.cs:87

    /// 清理所有实时任务
    /// </summary>
    public void ClearAllTriggers()
    {
        TaskTriggerDispatcher.Instance().ClearTriggers();
    }

    /// <summary>
    /// 添加实时任务,不会清理之前的任务
    /// </summary>
    /// <param name="timer"></param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <exception cref="ArgumentException"></exception>
    public void AddTrigger(RealtimeTimer timer)
    {
        var realtimeTimer = timer;
        if (realtimeTimer == null)
        {
            throw new ArgumentNullException(nameof(realtimeTimer), "实时任务对象不能为空");
        }

        if (string.IsNullOrEmpty(realtimeTimer.Name))
        {
            throw new ArgumentNullException(nameof(realtimeTimer.Name), "实时任务名称不能为空");
        }

        if (!TaskTriggerDispatcher.Instance().AddTrigger(realtimeTimer.Name, realtimeTimer.Config))
        {
            throw new ArgumentException($"添加实时任务失败: {realtimeTimer.Name}", nameof(realtimeTimer.Name));
        }
    }

    public async Task RunTask(SoloTask soloTask, CancellationTokenSource customCts)
    {
        // 创建链接的取消令牌源,任何一个取消都会触发
        CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
            customCts.Token,

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Construct the timer before calling: `const t = new RealtimeTimer('AutoPick'); dispatcher.AddTrigger(t);`
  2. Add a JS-side null check before calling AddTrigger.
  3. Use the RealtimeTimer constructor that takes a name to guarantee a usable object.

Example fix

// before (JS)
dispatcher.AddTrigger(maybeTimer);

// after (JS)
if (!maybeTimer) { throw new Error('timer must be constructed before AddTrigger'); }
dispatcher.AddTrigger(maybeTimer);
Defensive patterns

Strategy: validation

Validate before calling

// JS side
function addTriggerSafe(timer) {
  if (timer == null) throw new Error('RealtimeTimer must be constructed before AddTrigger');
  dispatcher.AddTrigger(timer);
}

Type guard

// JS duck-type check
function isRealtimeTimer(v) {
  return v != null && typeof v === 'object' && typeof v.Name !== 'undefined';
}

Prevention

When it happens

Trigger: JS calls `dispatcher.AddTrigger(null)` or `dispatcher.AddTrigger(undefined)`. A factory function in JS returns undefined and its result is passed straight through. A conditional branch that should have built a RealtimeTimer was skipped.

Common situations: JS script author forgot to construct the RealtimeTimer object. A typo or wrong variable name passed undefined. A feature flag toggled off left the timer variable unset.

Related errors


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