microsoft/aspire · error · ValueError

Cannot use value of type

Error message

Cannot use value of type {type(value).__name__} in reference expression. Expected a handle object, string, or number.

What it means

_extract_handle_for_expr raises ValueError when a value is neither a handle object, a string, a number, nor an already-formed {'$handle': ...} dict — for example lists, booleans in unexpected positions, dicts without $handle, or arbitrary objects. Reference-expression operands must be serializable into the expression protocol, and this type is not.

Solutions

  1. Pass the resource's handle object itself, not a list/dict wrapper containing it.
  2. Convert the value to a string or number first if it is a literal (e.g., str(value) or int(value)).
  3. Unwrap collections: pick the specific element that should participate in the expression.
  4. If you already have a transport dict, ensure it contains the '$handle' key as the protocol expects.

Example fix

// before
expr = string_expr("{r}", r=[redis, postgres])  # list not allowed
// after
expr = string_expr("{r}", r=redis)  # pass the single handle
Defensive patterns

Strategy: type-guard

Validate before calling

def ensure_operand(value):
    if isinstance(value, (list, dict)) and not (isinstance(value, dict) and '$handle' in value):
        raise TypeError(f'unsupported operand type {type(value).__name__}; pass a handle, str, or number')
    return value

Type guard

def is_expression_operand(value) -> bool:
    if value is None:
        return False
    if isinstance(value, (str, int, float)):
        return True
    if isinstance(value, dict):
        return '$handle' in value
    return hasattr(value, 'handle_id')

Try / catch

try:
    expr = string_expr('{r}', r=value)
except ValueError as e:
    if 'Cannot use value of type' in str(e):
        value = coerce_to_operand(value)  # unwrap/convert
        expr = string_expr('{r}', r=value)
    else:
        raise

Prevention

When it happens

Trigger: Passing a list, dict (without '$handle' key), bool, datetime, or custom object into a reference-expression argument; passing a marshalled transport dict where a raw handle was expected; feeding a parsed JSON structure directly as an operand.

Common situations: Accidentally passing a collection where a single handle was intended (e.g., a list of resources instead of one); passing a config dict instead of a value; passing booleans which are not accepted number-like literals here; SDK version change altering what counts as a handle.

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

Appendix: source

Thrown at src/Aspire.Hosting.CodeGeneration.Python/PythonModuleBuilder.cs:1452

                raise ValueError("Cannot use None in reference expression")

            # String literals - include directly in the expression
            if isinstance(value, str):
                return value

            # Number literals - convert to string
            if isinstance(value, (int, float)):
                return str(value)

            # Handle objects - get their JSON representation
            if isinstance(value, (Handle, _ReferenceHandle)):
                return value

            # Objects with $handle property (already in handle format)
            if isinstance(value, dict) and "$handle" in value:
                return value

            raise ValueError(
                f"Cannot use value of type {type(value).__name__} in reference expression. "
                f"Expected a handle object, string, or number."
            )


        def string_expr(value: str, **kwargs: typing.Any) -> ReferenceExpression:
            '''
            Helper function for creating reference expressions with named placeholders.

            Use this to create dynamic expressions that reference endpoints, parameters, and other
            value providers. The expression is evaluated at runtime by Aspire.

            Example:
                ```python
                redis = await builder.add_redis("cache")
                endpoint = await redis.get_endpoint("tcp")

                # Create a reference expression using named placeholders

View on GitHub (pinned to 25830f84bd)