devlooped/moq · error · ArgumentNullException

Value cannot be null. (Parameter 'value')

Error message

Value cannot be null. (Parameter 'value')

What it means

Moq exposes a replaceable ExpressionReconstructor.Instance, mirroring ExpressionCompiler.Instance. The setter enforces that a reconstructor is always available because delegate-based setups depend on it; assigning null throws ArgumentNullException for parameter 'value' to preserve that invariant.

Solutions

  1. Assign a valid ExpressionReconstructor instance (the built-in default is an ActionObserver-derived instance).
  2. Null-check the value before assignment; only swap when the new instance is non-null.
  3. To restore defaults, cache the original value at startup and reassign that, never null.
  4. Avoid resetting the static in teardowns unless you own a non-null replacement.

Example fix

// before
ExpressionReconstructor.Instance = null; // teardown reset
// after
ExpressionReconstructor.Instance = new ExpressionReconstructor(); // or restore saved default
Defensive patterns

Strategy: validation

Validate before calling

var reconstructor = ResolveReconstructor();
if (reconstructor == null) throw new InvalidOperationException("Resolved ExpressionReconstructor was null");
ExpressionReconstructor.Instance = reconstructor;

Type guard

static bool CanInstall(ExpressionReconstructor? r) => r is not null;

Try / catch

try
{
    ExpressionReconstructor.Instance = candidate;
}
catch (ArgumentNullException)
{
    // retain default instance
}

Prevention

When it happens

Trigger: Assigning null to `ExpressionReconstructor.Instance`, e.g. during test cleanup `ExpressionReconstructor.Instance = null;` or when a factory/container yields null.

Common situations: Reset code attempting to clear the override; DI resolution failures producing null; copying configuration code from ExpressionCompiler usage where null-reset seemed acceptable.

Related errors


AI-assisted analysis of devlooped/moq@89a5be629c (2026-09-16). Data as JSON: /api/errors/b1c5200ab956b7c3. Report an issue: GitHub.

Appendix: source

Thrown at src/Moq/ExpressionReconstructor.cs:20

// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

using System;
using System.Linq.Expressions;

namespace Moq
{
    /// <summary>
    ///   A <see cref="ExpressionReconstructor"/> reconstructs LINQ expression trees (<see cref="LambdaExpression"/>)
    ///   from <see cref="Action"/> delegates. It is the counterpart to <see cref="ExpressionCompiler"/>.
    /// </summary>
    abstract class ExpressionReconstructor
    {
        static ExpressionReconstructor instance = new ActionObserver();

        public static ExpressionReconstructor Instance
        {
            get => instance;
            set => instance = value ?? throw new ArgumentNullException(nameof(value));
        }

        protected ExpressionReconstructor()
        {
        }

        /// <summary>
        ///   Reconstructs a <see cref="LambdaExpression"/> from the given <see cref="Action{T}"/> delegate.
        /// </summary>
        /// <param name="action">The <see cref="Action"/> delegate for which to reconstruct a LINQ expression tree.</param>
        /// <param name="ctorArgs">Arguments to pass to a parameterized constructor of <typeparamref name="T"/>. (Optional.)</param>
        public abstract Expression<Action<T>> ReconstructExpression<T>(Action<T> action, object?[]? ctorArgs = null);
    }
}

View on GitHub (pinned to 89a5be629c)