microsoft/aspire · error · CapabilityError

result.$error

Error message

result.$error

What it means

When a capability RPC returns a structured AtsError payload (detected by isAtsError), the client throws a CapabilityError built from result.$error. This is the client-side projection of a server-side capability failure, preserving the structured error details.

Solutions

  1. Catch CapabilityError and inspect its $error fields (message/code/details) to learn the server-side failure cause.
  2. Validate the capability arguments against the generated types before calling.
  3. Regenerate the TypeScript client if the AppHost API changed (capability renamed/removed).
  4. Check the AppHost logs for the corresponding server-side exception stack.

Example fix

// before
const result = await client.invokeCapability('myCap', { id }); // throws raw CapabilityError

// after
try {
  const result = await client.invokeCapability('myCap', { id });
} catch (e) {
  if (e instanceof CapabilityError) {
    console.error('Capability failed:', e.$error.message, e.$error.code);
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// validate against generated types before the call
const parsed = capabilityArgsSchema.safeParse(args);
if (!parsed.success) throw new Error('invalid capability args');

Type guard

function isCapabilityError(e: unknown): e is CapabilityError {
  return e instanceof CapabilityError;
}

Try / catch

try {
  return await client.invokeCapability(cap, args);
} catch (e) {
  if (isCapabilityError(e)) {
    console.error('capability failed:', e.$error.code, e.$error.message);
  }
  throw e;
}

Prevention

When it happens

Trigger: invokeCapability sends 'invokeCapability' over JSON-RPC and the .NET side responds with an error-shaped result (capability threw, validation failed server-side, resource not available) instead of a normal result value.

Common situations: Server-side capability throws an exception; invalid args pass client validation but fail server validation; referenced resource/handle no longer exists on the AppHost; capability deprecated or removed after regeneration mismatch.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/transport.mts:1054

            // Ref counting: The vscode-jsonrpc socket keeps Node's event loop alive.
            // We ref() during RPC calls so the process doesn't exit mid-call, and
            // unref() when idle so the process can exit naturally after all work completes.
            if (this._pendingCalls === 0) {
                this.socket?.ref();
            }
            this._pendingCalls++;

            try {
                const result = await this.connection.sendRequest(
                    'invokeCapability',
                    capabilityId,
                    rpcArgs
                );

                // Check for structured error response
                if (isAtsError(result)) {
                    throw new CapabilityError(result.$error);
                }

                // Wrap handles automatically
                return wrapIfHandle(result, this) as T;
            } finally {
                this._pendingCalls--;
                if (this._pendingCalls === 0) {
                    this.socket?.unref();
                }
            }
        } finally {
            for (const cancellationId of cancellationIds) {
                unregisterCancellation(cancellationId);
            }
        }
    }

    disconnect(): void {

View on GitHub (pinned to 25830f84bd)