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
- Catch CapabilityError and inspect its $error fields (message/code/details) to learn the server-side failure cause.
- Validate the capability arguments against the generated types before calling.
- Regenerate the TypeScript client if the AppHost API changed (capability renamed/removed).
- 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
- Catch CapabilityError specifically and surface $error details to users.
- Validate arguments with generated schemas before invoking.
- Regenerate the client when the AppHost API changes.
- Check AppHost logs for the matching server-side exception.
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
- $error
- -32000
- getValue is only available on server-returned…
- Already connected to AppHost backchannel.
- An input name is required when uploading a file.
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)