{"record":{"id":"a919dab471612c91","repo":"sgl-project/sglang","slug":"kv-canary-type-obj-name-missing-required-m","errorCode":null,"errorMessage":"kv-canary: {type(obj).__name__} missing required method {method_name!r}","messagePattern":"kv-canary: (.+?) missing required method (.+?)","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/kv_canary/pool_patcher/utils.py","lineNumber":28,"sourceCode":"    obj: object,\n    method_name: str,\n    *,\n    wrapper: Callable[..., Any],\n) -> None:\n    \"\"\"Replace ``obj.method_name`` with a closure that delegates to ``wrapper``.\n\n    ``wrapper(original, *args, **kwargs)`` receives the original bound method as its first arg and the\n    call-site args/kwargs as the rest. It decides when (and whether) to call ``original`` and what to\n    return. The patched callable is installed as a plain function; :func:`functools.wraps` preserves\n    ``__name__`` / ``__doc__`` but the bound-method nature of the original is not retained.\n\n    Raises:\n        AttributeError: ``obj`` has no attribute ``method_name``.\n        RuntimeError: ``obj.method_name`` has already been wrapped by ``wrap_method`` (idempotency\n            guard — re-wrapping silently would stack two transforms and corrupt return values).\n    \"\"\"\n    if not hasattr(obj, method_name):\n        raise AttributeError(\n            f\"kv-canary: {type(obj).__name__} missing required method {method_name!r}\"\n        )\n    original = getattr(obj, method_name)\n    if getattr(original, _WRAPPED_MARKER_ATTR, None) is not None:\n        raise RuntimeError(\n            f\"kv-canary: {type(obj).__name__}.{method_name} already wrapped by kv-canary\"\n        )\n\n    @functools.wraps(original)\n    def patched(*args: Any, **kwargs: Any) -> Any:\n        return wrapper(original, *args, **kwargs)\n\n    setattr(patched, _WRAPPED_MARKER_ATTR, method_name)\n    setattr(obj, method_name, patched)\n","sourceCodeStart":10,"sourceCodeEnd":43,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/kv_canary/pool_patcher/utils.py#L10-L43","documentation":"kv-canary's wrap_method monkey-patches an existing method on an object; if the object lacks the named method entirely there is nothing to wrap, so it raises AttributeError with the class and method name.","triggerScenarios":"Calling wrap_method(model, 'forward') on an object whose class has no forward, or wrap_method(buf_info_obj, 'some_method') where the attribute was renamed or removed; also patch_buf_info_method / _patch_model_forward against an unexpected object type.","commonSituations":"Internal SGLang API changed the method name (e.g. forward refactor, BufferInfo method rename) and kv-canary was not updated; passing the wrong object (module vs instance, wrong wrapper class) to the patcher.","solutions":["Check hasattr(obj, method_name) before wrapping and pass the correct object that actually defines the method","Update kv-canary to the new method name if SGLang renamed it","Verify you are passing the instance (or class) that owns the method, not an unrelated wrapper"],"exampleFix":"// before\nwrap_method(attn_backend, 'forward')\n// after\nif hasattr(attn_backend, 'forward'):\n    wrap_method(attn_backend, 'forward')\nelse:\n    wrap_method(attn_backend.model, 'forward')","handlingStrategy":"type-guard","validationCode":"if not hasattr(obj, method_name):\n    raise AttributeError(f'{type(obj).__name__} has no {method_name}; wrong object?')\nwrap_method(obj, method_name, wrapper)","typeGuard":"def has_method(obj: object, name: str) -> bool:\n    return callable(getattr(obj, name, None))","tryCatchPattern":"try:\n    wrap_method(obj, method_name, wrapper)\nexcept AttributeError as e:\n    logger.error('cannot patch: %s', e)","preventionTips":["Assert the method exists before patching","Pin kv-canary and SGLang versions together so method names stay in sync"],"tags":["kv-canary","monkey-patching","attributeerror"],"backgroundTag":"attribute-not-found","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}