crewAIInc/crewAI · error · ValueError

filter_by and filter_value must be provided together.

Error message

filter_by and filter_value must be provided together.

What it means

DB2ToolSchema._validate_pair uses XOR ((filter_by is None) ^ (filter_value is None)) to enforce that the metadata filter is supplied as a complete pair. Providing exactly one of the two — a column without a value, or a value without a column — raises ValueError at Pydantic validation time. Both or neither must be present for the WHERE clause to be well-formed.

Source

Thrown at lib/crewai-tools/src/crewai_tools/tools/db2_search_tool/db2_search_tool.py:58

        description=(
            "Column name used for metadata filtering. "
            "Must be used together with filter_value."
        ),
    )

    filter_value: Any | None = Field(
        default=None,
        description=(
            "Value used for metadata filtering. Must be used together with filter_by."
        ),
    )

    @model_validator(mode="after")
    def _validate_filter_pair(self) -> DB2ToolSchema:
        if self.filter_by is not None and not self.filter_by.strip():
            raise ValueError("filter_by must be a non-empty column name.")
        if (self.filter_by is None) ^ (self.filter_value is None):
            raise ValueError("filter_by and filter_value must be provided together.")
        return self


class DB2VectorSearchTool(BaseTool):
    """
    Fortified IBM DB2 Vector Search Tool.
    Includes SQL injection protection, dynamic relational support, and type-safe serialization.
    """

    model_config = ConfigDict(arbitrary_types_allowed=True)

    name: str = "DB2VectorSearchTool"
    description: str = "Search IBM DB2 vector database for relevant documents. Uses a custom embedding function if supplied, otherwise OpenAI embeddings."
    args_schema: type[BaseModel] = DB2ToolSchema

    # Internal Whitelist for distance metrics to prevent SQL injection
    # Aligned with Db2 VECTOR_DISTANCE API:
    # https://www.ibm.com/docs/en/db2/12.1.x?topic=functions-vector-distance

View on GitHub (pinned to 754d7323be)

Solutions

  1. Supply both together: filter_by='department', filter_value='sales'.
  2. Or remove both to run an unfiltered vector search.
  3. Add a wrapper assertion: (filter_by is None) == (filter_value is None) before invoking the tool.

Example fix

# before
tool._run(query='Q3 revenue', filter_by='department')

# after
tool._run(query='Q3 revenue', filter_by='department', filter_value='finance')
Defensive patterns

Strategy: validation

Validate before calling

def build_filter_kwargs(filter_by: str | None, filter_value) -> dict:
    if (filter_by is None) != (filter_value is None):
        raise ValueError('filter_by and filter_value must be provided together')
    return {} if filter_by is None else {'filter_by': filter_by, 'filter_value': filter_value}

Type guard

def is_complete_filter_pair(kw: dict) -> bool:
    return (kw.get('filter_by') is None) == (kw.get('filter_value') is None)

Try / catch

try:
    tool._run(query=q, **kw)
except ValidationError as e:
    if 'provided together' in str(e):
        kw.pop('filter_by', None); kw.pop('filter_value', None)
        tool._run(query=q, **kw)  # retry unfiltered
    else:
        raise

Prevention

When it happens

Trigger: tool._run(query='...', filter_by='department') with no filter_value; or filter_value='sales' with no filter_by; agent JSON that includes one field and drops the other; kwargs built by merging dicts where one key gets overwritten.

Common situations: LLM tool calls that fill the filter column from the prompt but leave the value blank (or vice versa); refactors where filter_value is accidentally renamed; optional-argument handling that defaults one side to None and the other to ''.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/6f36aadf1625eacf. Report an issue: GitHub.