microsoft/aspire · error · PolyglotCapabilityInvocationException

Could not invoke ' ' because parameter ' ' expects , but…

Error message

Could not invoke '{methodName}'{targetContext} because parameter '{parameterName}' expects {expectedDescription}, but got {actualDescription}.

What it means

This error is thrown by ResolveHandleArgument in the remote host's polyglot (ATS) capability layer when an argument that should be a registered handle cannot be converted to the .NET type the capability method expects. Handles are opaque references clients hold to host-side objects (e.g., builders, resources); the host validates the handle's underlying object against the expected parameter type and throws a typed JSON-RPC type-mismatch exception listing the expected and actual descriptions when conversion fails.

Solutions

  1. Check which parameter the message names and confirm the handle you pass was obtained from a capability call that returns that exact type.
  2. Re-acquire the handle by re-invoking the creation API instead of reusing a cached handle across host restarts or reconnections.
  3. If you have a builder handle but the method wants a resource, call the appropriate build/as method first and pass the returned handle.
  4. Verify your client binding serializes handle references as handles (not raw JSON values) when constructing the args object.

Example fix

// before
var endpoint = await client.InvokeAsync("GetEndpoint", new JsonObject { ["resource"] = builderHandle });
// after
var resourceHandle = await client.InvokeAsync("GetResource", new JsonObject { ["builder"] = builderHandle });
var endpoint = await client.InvokeAsync("GetEndpoint", new JsonObject { ["resource"] = resourceHandle });
Defensive patterns

Strategy: validation

Validate before calling

function isHandleFor(value, expectedHandleName) {
  return value !== null && typeof value === 'object' && '$handle' in value && value.$handle?.type === expectedHandleName;
}

Type guard

function isValidHandle(value) {
  return value !== null && typeof value === 'object' && typeof (value as any).$handle?.id === 'string';
}

Try / catch

try {
  await client.InvokeAsync(method, args);
} catch (err) {
  if (err.code === -32602 && /expects .* but got/.test(err.message)) {
    const param = err.data?.parameterName;
    handles = await reacquireHandles(); // re-create stale handles
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling a remote capability method (via exportApi/polyglot invocation over JSON-RPC) and passing, as a handle-typed parameter: (1) an unregistered or stale handle object, (2) a handle whose underlying object is of a different type than the parameter expects (e.g., passing a resource builder where a resource is required and the builder's Resource property does not match), or (3) a plain JSON value instead of a handle.

Common situations: A client script caches a handle across sessions where the host restarted and the handle registry was reset; a caller passes a builder handle to a method expecting the built resource of an incompatible type; a language binding serializes the handle incorrectly so the raw object arrives without handle identity; passing the wrong handle variable after refactoring client code.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting.RemoteHost/Ats/PolyglotCapabilityInvocationException.cs:192

            innerException);
    }

    public static object ResolveHandleArgument(
        string capabilityId,
        string? polyglotMethodName,
        JsonObject? args,
        HandleRegistry handles,
        string parameterName,
        Type expectedType,
        object handleObject,
        string? targetParameterName = null)
    {
        if (TryConvertHandle(handleObject, expectedType, out var converted))
        {
            return converted!;
        }

        throw CreateTypeMismatch(
            capabilityId,
            polyglotMethodName,
            args,
            handles,
            parameterName,
            expectedType,
            handleObject,
            targetParameterName);
    }

    private static bool TryConvertHandle(object handleObject, Type expectedType, out object? converted)
    {
        if (expectedType.IsInstanceOfType(handleObject))
        {
            converted = handleObject;
            return true;
        }

View on GitHub (pinned to 25830f84bd)