BerriAI/litellm · error · LangFlowError

flow_id cannot be set via request parameters; use model lang

Error message

flow_id cannot be set via request parameters; use model langflow/{flow_id}

What it means

LiteLLM's LangFlow integration extracts the target flow exclusively from the model string ('langflow/{flow_id}'). Passing flow_id as a request parameter (e.g. in extra_body or kwargs, which land in optional_params) is rejected with HTTP 400 because it would let a caller redirect a shared API key to a different flow. The check happens in _get_flow_id before any HTTP request is made.

Source

Thrown at litellm/llms/langflow/chat/transformation.py:75

    def map_openai_params(
        self,
        non_default_params: dict,
        optional_params: dict,
        model: str,
        drop_params: bool,
    ) -> dict:
        return optional_params

    def _get_flow_id(self, model: str, optional_params: dict) -> str:
        """
        Extract flow_id from the authorized model name only.

        Model format: "langflow/{flow_id}". Request kwargs must not override
        flow_id (would allow calling another flow with the same API key).
        """
        if optional_params.get("flow_id") is not None:
            raise LangFlowError(
                status_code=400,
                message=("flow_id cannot be set via request parameters; use model langflow/{flow_id}"),
            )

        flow_id: Final = (model.split("/", 1)[1] if "/" in model else model).strip()
        if not flow_id:
            raise LangFlowError(
                status_code=400,
                message="flow_id is required; use model langflow/{flow_id}",
            )
        return flow_id

    def get_complete_url(
        self,
        api_base: str | None,
        api_key: str | None,
        model: str,
        optional_params: dict,

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Remove flow_id from the request kwargs / extra_body and encode it in the model name: model="langflow/{flow_id}"
  2. If using a router, check default_params and extra_body templates for a stale flow_id entry
  3. Verify no middleware or OpenAI-compatible shim is injecting flow_id into the payload

Example fix

# before
litellm.completion(model="langflow/abc-123", messages=msgs, flow_id="abc-123")

# after
litellm.completion(model="langflow/abc-123", messages=msgs)
Defensive patterns

Strategy: validation

Validate before calling

def safe_langflow_call(model: str, extra_body: dict | None = None) -> None:
    if "/" not in model or not model.split("/", 1)[1].strip():
        raise ValueError(f"model must be langflow/{{flow_id}}, got: {model!r}")
    body = extra_body or {}
    if "flow_id" in body:
        raise ValueError("flow_id must not be passed via extra_body; encode it in the model name")

Type guard

def is_valid_langflow_model(model: str) -> bool:
    return isinstance(model, str) and len(model.split("/", 1)) == 2 and bool(model.split("/", 1)[1].strip())

Try / catch

from litellm.llms.langflow.chat.transformation import LangFlowError
try:
    litellm.completion(model=f"langflow/{flow_id}", messages=msgs)
except LangFlowError as e:
    if e.status_code == 400 and "flow_id cannot be set" in str(e):
        # strip flow_id from kwargs and retry once with model-only routing
        ...

Prevention

When it happens

Trigger: Calling litellm.completion(model="langflow/my-flow", ..., flow_id="other-flow"), or passing flow_id via extra_body={'flow_id': ...}, api_key-level defaults, or router default_params that inject flow_id into optional_params.

Common situations: Migrating from a client that sent flow_id as a body parameter directly to the LangFlow REST API; putting flow_id in a router's default_params so it gets merged into every request; a generic OpenAI-compatible wrapper forwarding all kwargs.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/548149a0db888216. Report an issue: GitHub.