microsoft/aspire · error · Error

Cannot use value of type

Error message

Cannot use value of type ${typeof value} in reference expression. Expected a Handle, string, or number.

What it means

extractHandleForExpr builds a reference expression ($handle/$expr JSON) from a value passed where a resource reference is expected. It only accepts Handle objects, strings, and numbers; anything else (object, boolean, array, undefined) is rejected. This guards the wire protocol so only marshallable reference values reach the app host.

Solutions

  1. Pass the awaited Handle (or a string/number) instead of a raw object: const db = await resource.addDatabase(...); then pass db.
  2. Add 'await' to async builder calls so the argument is the resolved value, not a Promise.
  3. Check the generated SDK signature for the parameter to confirm which types are accepted.
  4. If passing dynamic data, wrap it as a parameter/expr through the supported APIs rather than a reference argument.

Example fix

// before
const db = resource.addDatabase("mydb"); // not awaited
builder.withReference(someOptionsObject); // object not a Handle

// after
const db = await resource.addDatabase("mydb");
builder.withReference(db); // Handle is accepted
Defensive patterns

Strategy: type-guard

Validate before calling

function isReferenceValue(v: unknown): boolean {
  return typeof v === "string" || typeof v === "number" || (v !== null && typeof v === "object" && "$handle" in (v as object));
}
if (!isReferenceValue(arg)) throw new TypeError("Reference argument must be a Handle, string, or number");

Type guard

function isHandleLike(v: unknown): v is Handle {
  return v !== null && typeof v === "object" && "$handle" in v;
}

Try / catch

try {
  builder.withReference(arg);
} catch (e) {
  if (e instanceof Error && e.message.includes("reference expression")) {
    throw new Error(`Argument is not a Handle/string/number; did you forget await? Got: ${typeof arg}`, { cause: e });
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing a non-primitive, non-Handle value into generated SDK builder methods that expect a reference: e.g. resource.addDatabase({ param: someObject }), passing a plain JS object or boolean where a Handle/string/number is required, or forgetting to await an async call so the argument is a Promise rather than the Handle.

Common situations: Copy-pasting config objects into reference slots; calling an async builder (addPostgres, addDatabase) without await and passing the pending result; passing undefined because an earlier call returned nothing; TypeScript loosened to unknown/any losing the Handle type.

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/a5ca9a36e7e1c200. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/base.mts:292

    // Handle objects - get their JSON representation
    if (isHandleLike(value)) {
        return value.toJSON();
    }

    // Objects with marshalled expression/handle payloads
    if (typeof value === 'object' && value !== null && ('$handle' in value || '$expr' in value)) {
        return value;
    }

    // Objects with toJSON that returns a marshalled expression or handle
    if (typeof value === 'object' && value !== null && 'toJSON' in value && typeof value.toJSON === 'function') {
        const json = value.toJSON();
        if (json && typeof json === 'object' && ('$handle' in json || '$expr' in json)) {
            return json;
        }
    }

    throw new Error(
        `Cannot use value of type ${typeof value} in reference expression. ` +
        `Expected a Handle, string, or number.`
    );
}

function isHandleLike(value: unknown): value is Handle {
    return (
        value !== null &&
        typeof value === 'object' &&
        '$handle' in value &&
        typeof (value as { $handle?: unknown }).$handle === 'string' &&
        '$type' in value &&
        typeof (value as { $type?: unknown }).$type === 'string' &&
        'toJSON' in value &&
        typeof (value as { toJSON?: unknown }).toJSON === 'function'
    );
}

View on GitHub (pinned to 25830f84bd)