microsoft/aspire · error · AppHostUsageError

Argument ' ' passed to capability ' ' is a Promise-like…

Error message

Argument '${path}' passed to capability '${capabilityId}' is a Promise-like value. This usually means an async builder call was not awaited. Did you forget 'await' on a call like builder.addPostgres(...) or resource.addDatabase(...)?

What it means

Before sending a capability invocation, validateValue walks the argument tree and rejects any Promise-like value. A Promise in the payload almost always means an async builder call (builder.addPostgres(...), resource.addDatabase(...)) was not awaited, so the pending promise — not the resource handle — was passed as an argument. The library fails fast with a targeted hint instead of sending garbage over the wire.

Solutions

  1. Add 'await' to the async builder call whose result is being passed: pass the resolved resource, not the Promise.
  2. If collecting multiple resources, await inside the callback or use Promise.all before consuming the values.
  3. Enable TypeScript's no-floating-promises lint rule to catch un-awaited calls at authoring time.
  4. Check the argument named in the error ('path') to find the exact un-awaited expression.

Example fix

// before
const app = builder.addApp("app", { db: builder.addPostgres("pg") }); // Promise passed

// after
const pg = await builder.addPostgres("pg");
const app = builder.addApp("app", { db: pg });
Defensive patterns

Strategy: validation

Validate before calling

function assertNoPromises(value: unknown, path = "arg", seen = new Set<object>()): void {
  if (value === null || typeof value !== "object") return;
  if (typeof (value as { then?: unknown }).then === "function") throw new TypeError(`'${path}' is an un-awaited Promise`);
  if (seen.has(value)) return;
  seen.add(value);
  for (const [k, v] of Object.entries(value)) assertNoPromises(v, `${path}.${k}`, seen);
}

Type guard

function isPromiseLike(v: unknown): v is PromiseLike<unknown> {
  return v !== null && typeof v === "object" && typeof (v as { then?: unknown }).then === "function";
}

Try / catch

try {
  await capability(args);
} catch (e) {
  if (e instanceof Error && e.message.includes("Promise-like value")) {
    const m = e.message.match(/Argument '([^']+)'/);
    throw new Error(`Un-awaited async call at '${m?.[1]}'; add await to the builder call producing that argument`, { cause: e });
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing the direct return value of an async builder method into another capability call without await: addResource(pg.addDatabase("db")) where addDatabase is async; putting builder call results into arrays/objects passed as arguments; forgetting await inside map() callbacks.

Common situations: Migrating code from sync to async builder APIs; chaining calls across lines and missing one await; using Promise.all results incorrectly; new team members unfamiliar with which SDK calls are async.

Related errors


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

Appendix: source

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

        typeof (value as { then?: unknown }).then === 'function'
    );
}

function validateCapabilityArgs(
    capabilityId: string,
    args?: Record<string, unknown>
): void {
    if (!args) {
        return;
    }

    const validateValue = (value: unknown, path: string, ancestors: Set<object>): void => {
        if (value === null || value === undefined) {
            return;
        }

        if (isPromiseLike(value)) {
            throw new AppHostUsageError(
                `Argument '${path}' passed to capability '${capabilityId}' is a Promise-like value. ` +
                `This usually means an async builder call was not awaited. ` +
                `Did you forget 'await' on a call like builder.addPostgres(...) or resource.addDatabase(...)?`
            );
        }

        if (typeof value !== 'object') {
            return;
        }

        if (ancestors.has(value)) {
            throw createCircularReferenceError(capabilityId, path);
        }

        ancestors.add(value);
        try {
            if (Array.isArray(value)) {
                for (let i = 0; i < value.length; i++) {

View on GitHub (pinned to 25830f84bd)