mlflow/mlflow · error · MlflowException

Patching async property methods is not supported.

Error message

Patching async property methods is not supported.

What it means

`safe_patch` supports patching `@property`-decorated methods, but only synchronous ones. When the property getter is an async coroutine function, MLflow raises this because there is no supported way to wrap an async property getter safely for autologging.

Source

Thrown at mlflow/utils/autologging_utils/safety.py:292

        tags = _resolve_extra_tags(autologging_integration, extra_tags)
        patch_function = with_managed_run(
            autologging_integration,
            patch_function,
            tags=tags,
        )

    original_fn = gorilla.get_original_attribute(
        destination, function_name, bypass_descriptor_protocol=False
    )
    # Retrieve raw attribute while bypassing the descriptor protocol
    raw_original_obj = gorilla.get_original_attribute(
        destination, function_name, bypass_descriptor_protocol=True
    )
    if original_fn != raw_original_obj:
        raise RuntimeError(f"Unsupported patch on {destination}.{function_name}")
    elif isinstance(original_fn, property):
        if is_async_function:
            raise MlflowException("Patching async property methods is not supported.")

        is_property_method = True

        # For property decorated methods (a kind of method delegation), e.g.
        # class A:
        #   @property
        #   def f1(self):
        #     ...
        #     return delegated_f1
        #
        # suppose `a1` is an instance of class `A`,
        # `A.f1.fget` will get the original `def f1(self)` method,
        # and `A.f1.fget(a1)` will be equivalent to `a1.f1()` and
        # its return value will be the `delegated_f1` function.
        # So using the `property.fget` we can construct the (delegated) "original_fn"
        def original(self, *args, **kwargs):
            # the `original_fn.fget` will get the original method decorated by `property`
            # the `original_fn.fget(self)` will get the delegated function returned by the

View on GitHub (pinned to 6a27f2decc)

Solutions

  1. Patch the method that returns/awaits the property value instead of the property itself
  2. Patch a different synchronous attribute or an internal sync method of the class
  3. Skip autologging that specific attribute (remove it from the patch list)

Example fix

// before
safe_patch(FLAVOR, AsyncClient, 'session', _patch_session_fn)  # @property async def session
// after
safe_patch(FLAVOR, AsyncClient, 'ensure_session', _patch_fn)  # patch the async method, not the property
Defensive patterns

Strategy: validation

Validate before calling

def patch_target_is_supported(cls, name):
    attr = inspect.getattr_static(cls, name)
    if isinstance(attr, property) and inspect.iscoroutinefunction(attr.fget):
        return False
    return True

Type guard

def is_sync_property(attr) -> bool:
    return isinstance(attr, property) and not inspect.iscoroutinefunction(attr.fget)

Prevention

When it happens

Trigger: Calling `safe_patch` (directly or via an integration's `autolog()`) on a class attribute defined with `@property` whose getter is `async def` (e.g. `@property async def client(...)` in a framework class).

Common situations: Newer async LLM SDK versions converting a lazy sync client property into an async one; writing a custom autolog integration that targets async lazy-initializer properties.

Related errors


AI-assisted analysis of mlflow/mlflow@6a27f2decc (2026-08-29). Data as JSON: /api/errors/66fe2d8ea2a9cf14. Report an issue: GitHub.