langflow-ai/langflow · error · ValueError
provider_data must include exactly one of 'input' or 'messag
Error message
provider_data must include exactly one of 'input' or 'message'.
What it means
A Pydantic model_validator (mode='after') on WatsonxApiExecutionInput enforces an XOR: provider_data must carry exactly one of the string field 'input' or the object field 'message'. The check `has_input == has_message` fails when both are set or both are None, so the request is rejected as a ValidationError before the agent execution call is dispatched.
Source
Thrown at src/backend/base/langflow/api/v1/mappers/deployments/watsonx_orchestrate/payloads.py:594
tool_display_name: NonEmptyString
class WatsonxApiExecutionInput(BaseModel):
"""API-facing provider_data payload for POST deployment runs."""
model_config = {"extra": "forbid"}
input: str | None = None
message: dict[str, Any] | None = None
thread_id: str | None = None
@model_validator(mode="after")
def validate_input_or_message_exclusive(self) -> WatsonxApiExecutionInput:
has_input = self.input is not None
has_message = self.message is not None
if has_input == has_message:
msg = "provider_data must include exactly one of 'input' or 'message'."
raise ValueError(msg)
return self
class _WatsonxApiAgentExecutionResultBase(BaseModel):
"""Shared fields for API-facing agent execution result payloads.
All provider-owned identifiers and metadata live here inside
``provider_data``. The enclosing response only carries Langflow-owned
fields (``deployment_id``). ``deployment_id`` (Langflow DB UUID) is
intentionally omitted from this schema to avoid ownership confusion.
"""
model_config = {"extra": "allow"}
id: NonEmptyString | None = None
agent_id: NonEmptyString | None = None
thread_id: NonEmptyString | None = None
status: str | None = NoneView on GitHub (pinned to 976ec789d2)
Solutions
- For plain-text runs, send provider_data with only input: {"input": "hello", "thread_id": "..."}.
- For structured runs, send only message: {"message": {...}, "thread_id": "..."}.
- Audit client builders for a code path that sets both fields (e.g. copy-template then fill-in).
- Remove explicit null keys from the JSON before sending (null and absent are equivalent here, but neither counts as provided).
Example fix
# before (both set -> error)
provider_data = {"input": "hello", "message": {"role": "user", "content": "hello"}}
# after (text path)
provider_data = {"input": "hello"}
# or (structured path)
provider_data = {"message": {"role": "user", "content": "hello"}} Defensive patterns
Strategy: validation
Validate before calling
def valid_execution_payload(pd: dict) -> bool:
return (pd.get('input') is None) != (pd.get('message') is None) Type guard
from typing import TypedDict
class TextExecution(TypedDict):
input: str
thread_id: str | None
class MessageExecution(TypedDict):
message: dict
thread_id: str | None
def is_text_execution(pd: dict) -> bool: return 'input' in pd and 'message' not in pd Try / catch
Catch pydantic.ValidationError at the API boundary; if the message contains 'exactly one of', re-prompt/422 with a schema hint instead of retrying.
Prevention
- Model the two execution modes as separate builder functions (text vs structured) so both are never set.
- Drop null keys when serializing payloads.
When it happens
Trigger: POST to the Watsonx deployment execution endpoint with provider_data containing neither key ({}, only thread_id), or containing both ({'input': 'hi', 'message': {...}}).
Common situations: Defaulting both fields (they are Optional/None) and forgetting to set one; merging two client code paths (a simple-text path and a structured-message path) into one payload; sending {'input': None} explicitly, which still counts as None.
Related errors
- flow_version_id must be provided as a UUID string or UUID ob
- flow_version_id must be a valid UUID.
- actions must be strings
- str(e)
- String must not be empty.
AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14).
Data as JSON: /api/errors/9717cc9f5b71525a.
Report an issue: GitHub.