microsoft/aspire · error · ArgumentNullException

Object reference not passed for parameter 'callback'…

Error message

Object reference not passed for parameter 'callback' (ArgumentNullException: callback)

What it means

RustCargoArgsCallbackAnnotation's constructor validates its 'callback' parameter with ArgumentNullException.ThrowIfNull and its Callback property initializer repeats the guard. The library requires a non-null delegate because the annotation is invoked later during cargo argument construction; a null callback would crash the pipeline far from the misuse site. The exception names the offending parameter so the mistake is caught at annotation creation time.

Solutions

  1. Pass a non-null async lambda when creating the annotation, e.g. args => Task.CompletedTask if no customization is needed
  2. Check any intermediate variable holding the callback for null before constructing the annotation
  3. Move the annotation registration inside the branch where the callback is actually created

Example fix

// before
Func<RustCargoArgsCallbackContext, Task> callback = condition ? ConfigureArgs : null;
var annotation = new RustCargoArgsCallbackAnnotation(callback);
// after
Func<RustCargoArgsCallbackContext, Task> callback = condition ? ConfigureArgs : static _ => Task.CompletedTask;
var annotation = new RustCargoArgsCallbackAnnotation(callback);
Defensive patterns

Strategy: validation

Validate before calling

if (callback is null) throw new InvalidOperationException("Cargo args callback must be assigned before creating RustCargoArgsCallbackAnnotation.");

Type guard

bool HasCallback(Func<RustCargoArgsCallbackContext, Task>? cb) => cb is not null;

Try / catch

try { var ann = new RustCargoArgsCallbackAnnotation(cb); } catch (ArgumentNullException ex) when (ex.ParamName == "callback") { log.LogError(ex, "Null cargo args callback"); }

Prevention

When it happens

Trigger: Calling new RustCargoArgsCallbackAnnotation(resource, callback, ...) (or the WithCargoArgsCallback-style extension that constructs it) with a null Func<RustCargoArgsCallbackContext, Task>, typically from an uninitialized delegate field or a helper that conditionally assigns the callback.

Common situations: Conditional fluent configuration where the callback is only assigned in one branch; a lambda variable that is still null at annotation-creation time; refactoring that removed the lambda but left the call passing the now-null variable.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/95b8e07bd9e9d985. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Rust/RustCargoArgsCallbackAnnotation.cs:31

{
    /// <summary>
    /// Initializes a new instance of the <see cref="RustCargoArgsCallbackAnnotation"/> class.
    /// </summary>
    /// <param name="callback">The callback action to be executed.</param>
    public RustCargoArgsCallbackAnnotation(Action<IList<string>> callback)
        : this(context =>
        {
            callback(context.Args);
            return Task.CompletedTask;
        })
    {
        ArgumentNullException.ThrowIfNull(callback);
    }

    /// <summary>
    /// Gets the callback action that is executed to populate cargo-level arguments.
    /// </summary>
    public Func<RustCargoArgsCallbackContext, Task> Callback { get; } = callback ?? throw new ArgumentNullException(nameof(callback));
}

/// <summary>
/// Represents callback context for cargo-level command-line arguments.
/// </summary>
/// <param name="resource">The Rust application resource whose cargo arguments are being built.</param>
/// <param name="args">The command-line arguments collection.</param>
/// <param name="cancellationToken">The cancellation token associated with this callback context.</param>
/// <remarks>
/// Unlike program arguments, cargo arguments are plain strings rather than <see cref="object"/>.
/// They select build behaviour (<c>--release</c>, <c>--features</c>, <c>--bin</c>) before the program
/// starts, so there is nothing for a deferred value such as an endpoint reference to resolve against;
/// those belong after the <c>--</c> separator and are added with <c>WithArgs</c>.
/// </remarks>
public sealed class RustCargoArgsCallbackContext(RustAppResource resource, IList<string> args, CancellationToken cancellationToken = default)
{
    /// <summary>
    /// Gets the Rust application resource whose cargo arguments are being built.

View on GitHub (pinned to 25830f84bd)