{"record":{"id":"a5ca9a36e7e1c200","repo":"microsoft/aspire","slug":"cannot-use-value-of-type-typeof-value-in-reference","errorCode":null,"errorMessage":"Cannot use value of type ${typeof value} in reference expression. Expected a Handle, string, or number.","messagePattern":"Cannot use value of type (.+?) in reference expression\\. Expected a Handle, string, or number\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/base.mts","lineNumber":292,"sourceCode":"    // Handle objects - get their JSON representation\n    if (isHandleLike(value)) {\n        return value.toJSON();\n    }\n\n    // Objects with marshalled expression/handle payloads\n    if (typeof value === 'object' && value !== null && ('$handle' in value || '$expr' in value)) {\n        return value;\n    }\n\n    // Objects with toJSON that returns a marshalled expression or handle\n    if (typeof value === 'object' && value !== null && 'toJSON' in value && typeof value.toJSON === 'function') {\n        const json = value.toJSON();\n        if (json && typeof json === 'object' && ('$handle' in json || '$expr' in json)) {\n            return json;\n        }\n    }\n\n    throw new Error(\n        `Cannot use value of type ${typeof value} in reference expression. ` +\n        `Expected a Handle, string, or number.`\n    );\n}\n\nfunction isHandleLike(value: unknown): value is Handle {\n    return (\n        value !== null &&\n        typeof value === 'object' &&\n        '$handle' in value &&\n        typeof (value as { $handle?: unknown }).$handle === 'string' &&\n        '$type' in value &&\n        typeof (value as { $type?: unknown }).$type === 'string' &&\n        'toJSON' in value &&\n        typeof (value as { toJSON?: unknown }).toJSON === 'function'\n    );\n}\n","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.CodeGeneration.TypeScript/Resources/base.mts#L274-L310","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass the awaited Handle (or a string/number) instead of a raw object: const db = await resource.addDatabase(...); then pass db.","Add 'await' to async builder calls so the argument is the resolved value, not a Promise.","Check the generated SDK signature for the parameter to confirm which types are accepted.","If passing dynamic data, wrap it as a parameter/expr through the supported APIs rather than a reference argument."],"exampleFix":"// before\nconst db = resource.addDatabase(\"mydb\"); // not awaited\nbuilder.withReference(someOptionsObject); // object not a Handle\n\n// after\nconst db = await resource.addDatabase(\"mydb\");\nbuilder.withReference(db); // Handle is accepted","handlingStrategy":"type-guard","validationCode":"function isReferenceValue(v: unknown): boolean {\n  return typeof v === \"string\" || typeof v === \"number\" || (v !== null && typeof v === \"object\" && \"$handle\" in (v as object));\n}\nif (!isReferenceValue(arg)) throw new TypeError(\"Reference argument must be a Handle, string, or number\");","typeGuard":"function isHandleLike(v: unknown): v is Handle {\n  return v !== null && typeof v === \"object\" && \"$handle\" in v;\n}","tryCatchPattern":"try {\n  builder.withReference(arg);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"reference expression\")) {\n    throw new Error(`Argument is not a Handle/string/number; did you forget await? Got: ${typeof arg}`, { cause: e });\n  }\n  throw e;\n}","preventionTips":["Always await async builder calls before passing their results.","Type parameters as Handle | string | number so the compiler rejects bad values.","Never pass config objects into reference-typed arguments."],"tags":["typescript","type-mismatch","reference-expression"],"backgroundTag":"invalid-argument-value","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}