microsoft/aspire · error · InvalidOperationException

Resource command argument

Error message

Resource command argument '{name}' must be a string, number, boolean, or null.

What it means

Individual resource command argument values must serialize to string, number, boolean, or null, because they are converted into InteractionInputCollection entries. Nested objects or arrays inside the arguments payload cannot be converted and throw this error.

Solutions

  1. Flatten nested structures into scalar arguments (string/number/boolean/null).
  2. Serialize complex values to a JSON string yourself if the command supports it, e.g. {"optionsJson": "{\"a\":1}"}.
  3. Check the command's declared input schema and send only the defined scalar inputs.
  4. Update the client tooling to reject nested values before sending.

Example fix

// before
new JsonObject { ["options"] = new JsonObject { ["a"] = 1 } }
// after
new JsonObject { ["optionsJson"] = "{\"a\":1}" }
Defensive patterns

Strategy: validation

Validate before calling

bool AllScalar(JsonNode node) => node.GetValueKind() switch
{
    JsonValueKind.Object => node.AsObject().All(p => AllScalar(p.Value!)),
    JsonValueKind.Array => false,
    _ => true
};

Type guard

static bool IsScalarArgument(JsonNode v) => v.GetValueKind() is JsonValueKind.String or JsonValueKind.Number or JsonValueKind.True or JsonValueKind.False or JsonValueKind.Null;

Try / catch

try { await rpc.ExecuteResourceCommandAsync(request); }
catch (InvalidOperationException ex) when (ex.Message.Contains("must be a string, number, boolean")) { logger.LogError(ex, "Flatten nested argument values before sending."); }

Prevention

When it happens

Trigger: Calling ExecuteResourceCommandAsync with an arguments object/array containing a nested JSON object or array value for any argument name, e.g. {"options": {"a": 1}}.

Common situations: Clients assuming complex/nested payloads are supported; passing arrays of values for multi-select inputs; generic tools forwarding raw JSON config blobs as command arguments.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting/Backchannel/AuxiliaryBackchannelRpcTarget.cs:420

        return arguments
            .Select((value, index) => ConvertArgumentValue($"[{index}]", value))
            .ToArray();
    }

    private static string? ConvertArgumentValue(string name, JsonNode? value)
    {
        if (value is null)
        {
            return null;
        }

        return value.GetValueKind() switch
        {
            JsonValueKind.String => value.GetValue<string>(),
            JsonValueKind.Number => value.ToJsonString(),
            JsonValueKind.True => "true",
            JsonValueKind.False => "false",
            _ => throw new InvalidOperationException($"Resource command argument '{name}' must be a string, number, boolean, or null.")
        };
    }

    /// <summary>
    /// Returns the discovery info needed to attach to a resource's terminal session(s). For a
    /// resource configured with <c>WithTerminal()</c>, this enumerates the per-replica
    /// consumer-side UDS endpoints by asking each per-replica terminal host process over its
    /// control UDS in parallel. Returns <see cref="GetTerminalInfoResponse.IsAvailable"/> = false
    /// when the resource has no <see cref="TerminalAnnotation"/>; per-host control RPC failures
    /// (e.g. host not yet started) degrade to <see cref="TerminalReplicaInfo.IsAlive"/> = false
    /// for that replica without failing the whole call.
    /// </summary>
    public async Task<GetTerminalInfoResponse> GetTerminalInfoAsync(GetTerminalInfoRequest request, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(request);

        var appModel = serviceProvider.GetRequiredService<DistributedApplicationModel>();
        var resource = appModel.Resources.FirstOrDefault(r => string.Equals(r.Name, request.ResourceName, StringComparisons.ResourceName));

View on GitHub (pinned to 25830f84bd)