{"record":{"id":"e478f273c2cafd58","repo":"sgl-project/sglang","slug":"pair-postprocess-must-be-callable-or-none","errorCode":null,"errorMessage":"pair_postprocess must be callable or None","messagePattern":"pair_postprocess must be callable or None","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/models/schedulers/flow_match_pair.py","lineNumber":218,"sourceCode":"            reverse_sigmas=reverse_sigmas,\n            exponential_shift=exponential_shift,\n            exponential_shift_mu=exponential_shift_mu,\n            shift_terminal=shift_terminal,\n        )\n\n    def set_pair_postprocess(self, fn):\n        \"\"\"Set a postprocess function to customize pairs after construction.\n\n        Args:\n            fn: Callable with signature fn(pairs: torch.Tensor) -> torch.Tensor.\n                The returned tensor must have the same shape as input pairs.\n\n        Raises:\n            TypeError: If fn is not callable or None.\n            RuntimeError: If scheduler is not initialized.\n        \"\"\"\n        if fn is not None and not callable(fn):\n            raise TypeError(\"pair_postprocess must be callable or None\")\n        self._pair_postprocess_fn = fn\n        self._pair_postprocess_requires_source = (\n            False if fn is None else bool(getattr(fn, \"_requires_source\", False))\n        )\n        if self.timesteps is None or self.sigmas is None:\n            raise RuntimeError(\"Scheduler not initialized; call set_timesteps() first\")\n        self._refresh_pair_cache()\n\n    def set_pair_postprocess_by_name(self, name: str | None, **kwargs):\n        \"\"\"Configure a postprocess function by name.\n\n        Supported names:\n            - None/\"none\"/\"off\"/\"false\"/\"no\": disable\n            - \"quadratic_perp_bulge_swap\": x2=x+d, y2=x-d, where d=4*amp*s*(1-s), s=t/T\n            - \"v2a_sequential\": assume pairs are (t,t); sample half sequence from column 0\n              with stride 2, then let column 0 follow this sequence first, followed by column 1\n            - \"a2v_sequential\": same as above, but column 1 first then column 0\n            - \"dual_sigma_shift\": use only timestep count; rebuild two columns independently using","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/models/schedulers/flow_match_pair.py#L200-L236","documentation":"FlowMatchEulerPairScheduler.set_pair_postprocess accepts only a callable (the postprocess applied to sampler outputs each step) or None to disable it. Passing anything else — a string name, a list of functions, a bound non-callable attribute — raises TypeError.","triggerScenarios":"Calling scheduler.set_pair_postprocess(fn) where fn is e.g. a function name string like 'rescale_noise_sigma' instead of the function object, or a tuple of (fn, kwargs). Use set_pair_postprocess_by_name for string-based configuration.","commonSituations":"Config-driven code that passes the postprocess name straight through; serializing/deserializing a pipeline and losing the function reference; wrapping the fn in a dict for kwargs instead of using functools.partial.","solutions":["Pass the function object itself (or functools.partial(fn, **kwargs)) or None","If you have a name string, call set_pair_postprocess_by_name(name, **kwargs) instead","Ensure custom postprocess functions are plain callables taking the expected signature"],"exampleFix":"// before\nsched.set_pair_postprocess('rescale_noise_sigma')\n// after\nsched.set_pair_postprocess_by_name('rescale_noise_sigma')\n# or\nfrom x import rescale_noise_sigma\nsched.set_pair_postprocess(rescale_noise_sigma)","handlingStrategy":"type-guard","validationCode":"assert fn is None or callable(fn), f'pair_postprocess must be callable or None, got {type(fn)}'","typeGuard":"def is_postprocess_arg(fn) -> bool:\n    return fn is None or callable(fn)","tryCatchPattern":"try:\n    sched.set_pair_postprocess(fn)\nexcept TypeError:\n    if isinstance(fn, str):\n        sched.set_pair_postprocess_by_name(fn)\n    else:\n        raise","preventionTips":["Use set_pair_postprocess_by_name for string configs","Wrap fn + kwargs with functools.partial instead of tuples/dicts","Type-annotate the parameter as Callable | None in your own wrappers"],"tags":["scheduler","type-error","postprocess","flow-match"],"backgroundTag":"invalid-callback-type","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}