sgl-project/sglang · error · TypeError

pair_postprocess must be callable or None

Error message

pair_postprocess must be callable or None

What it means

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.

Source

Thrown at python/sglang/multimodal_gen/runtime/models/schedulers/flow_match_pair.py:218

            reverse_sigmas=reverse_sigmas,
            exponential_shift=exponential_shift,
            exponential_shift_mu=exponential_shift_mu,
            shift_terminal=shift_terminal,
        )

    def set_pair_postprocess(self, fn):
        """Set a postprocess function to customize pairs after construction.

        Args:
            fn: Callable with signature fn(pairs: torch.Tensor) -> torch.Tensor.
                The returned tensor must have the same shape as input pairs.

        Raises:
            TypeError: If fn is not callable or None.
            RuntimeError: If scheduler is not initialized.
        """
        if fn is not None and not callable(fn):
            raise TypeError("pair_postprocess must be callable or None")
        self._pair_postprocess_fn = fn
        self._pair_postprocess_requires_source = (
            False if fn is None else bool(getattr(fn, "_requires_source", False))
        )
        if self.timesteps is None or self.sigmas is None:
            raise RuntimeError("Scheduler not initialized; call set_timesteps() first")
        self._refresh_pair_cache()

    def set_pair_postprocess_by_name(self, name: str | None, **kwargs):
        """Configure a postprocess function by name.

        Supported names:
            - None/"none"/"off"/"false"/"no": disable
            - "quadratic_perp_bulge_swap": x2=x+d, y2=x-d, where d=4*amp*s*(1-s), s=t/T
            - "v2a_sequential": assume pairs are (t,t); sample half sequence from column 0
              with stride 2, then let column 0 follow this sequence first, followed by column 1
            - "a2v_sequential": same as above, but column 1 first then column 0
            - "dual_sigma_shift": use only timestep count; rebuild two columns independently using

View on GitHub (pinned to 0132848349)

Solutions

  1. Pass the function object itself (or functools.partial(fn, **kwargs)) or None
  2. If you have a name string, call set_pair_postprocess_by_name(name, **kwargs) instead
  3. Ensure custom postprocess functions are plain callables taking the expected signature

Example fix

// before
sched.set_pair_postprocess('rescale_noise_sigma')
// after
sched.set_pair_postprocess_by_name('rescale_noise_sigma')
# or
from x import rescale_noise_sigma
sched.set_pair_postprocess(rescale_noise_sigma)
Defensive patterns

Strategy: type-guard

Validate before calling

assert fn is None or callable(fn), f'pair_postprocess must be callable or None, got {type(fn)}'

Type guard

def is_postprocess_arg(fn) -> bool:
    return fn is None or callable(fn)

Try / catch

try:
    sched.set_pair_postprocess(fn)
except TypeError:
    if isinstance(fn, str):
        sched.set_pair_postprocess_by_name(fn)
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/e478f273c2cafd58. Report an issue: GitHub.