BUG-HUNT: [type-safety] RouteDefinition validation assumes unvalidated config structure leading to runtime crashes #7157

Open
opened 2026-04-10 08:18:11 +00:00 by HAL9000 · 1 comment
Owner

Background and Context

RouteDefinition.validate_references() in src/cleveragents/actor/schema.py accesses node.config for conditional nodes without proper type guards. The code treats node.config as dict[str, Any] but iterates over its "conditions" key and accesses "route_to" on each element without validating that these values conform to the expected structure. When a malformed YAML actor definition is loaded, this produces cryptic TypeError exceptions instead of actionable ValidationError messages.

Current Behavior

def validate_references(self) -> None:
    # Build adjacency list...
    for node in self.nodes:
        if node.type == NodeType.CONDITIONAL and isinstance(node.config, dict):
            for cond in node.config.get("conditions", []):  # Assumes list
                if isinstance(cond, dict) and "route_to" in cond:
                    target = str(cond["route_to"])  # Unsafe cast
                    if target in node_ids:
                        adj[node.id].append(target)

Type safety issues:

  1. Unvalidated list accessnode.config.get("conditions", []) assumes the result is iterable, but the value could be any type (e.g., a string "not_a_list"), causing TypeError on iteration.
  2. Unsafe string conversionstr(cond["route_to"]) silently converts complex objects (e.g., {"nested": {"object": True}}) to strings like "{'nested': {'object': True}}", hiding type errors and producing nonsensical node IDs.
  3. No structure validation — The code assumes conditional nodes have a specific config schema without validating it, violating the project's fail-fast and explicit-over-implicit principles.

Runtime crash scenarios:

# Scenario 1: conditions is not a list
nodes:
  - id: "bad_conditional"
    type: "conditional"
    config:
      conditions: "not_a_list"  # TypeError on iteration

# Scenario 2: condition items are not dicts
nodes:
  - id: "bad_conditional"
    type: "conditional"
    config:
      conditions: ["string", 123, null]  # TypeError accessing route_to

# Scenario 3: route_to is complex object
nodes:
  - id: "bad_conditional"
    type: "conditional"
    config:
      conditions:
        - route_to: {nested: {object: true}}  # str() hides the issue

Expected Behavior

  • Malformed conditional node configs raise a clear ValidationError with an actionable message identifying the offending node and the structural problem.
  • validate_references() uses explicit type guards before accessing nested config values.
  • Complex objects in route_to are rejected with a descriptive error rather than silently coerced to strings.
  • All public method arguments are validated at entry per CONTRIBUTING.md error-handling guidelines.

Acceptance Criteria

  • validate_references() validates that node.config["conditions"] is a list before iterating; raises ValidationError if not.
  • Each element in conditions is validated to be a dict with a str-typed "route_to" key; raises ValidationError for any violation.
  • No str() coercion is applied to route_to; the value must already be a str or a ValidationError is raised.
  • All three crash scenarios above produce ValidationError with clear, actionable messages.
  • Pyright type-checks the updated method without errors or # type: ignore suppressions.
  • BDD scenarios cover all three crash scenarios and the happy path.

Supporting Information

  • File: src/cleveragents/actor/schema.py
  • Method: RouteDefinition.validate_references()
  • Related: CONTRIBUTING.md — "Argument Validation", "Fail-Fast Principles", "Type Safety"
  • Spec: Actor GRAPH conditional node config schema (docs/specification.md — Actor section)

Backlog note: This issue was discovered during autonomous operation
on milestone v3.2.0. It does not block milestone completion and has been
placed in the backlog for human review and future milestone assignment.

Metadata

  • Branch: bugfix/actor-route-definition-type-safety
  • Commit Message: fix(actor): add type guards to RouteDefinition.validate_references() for conditional node config
  • Milestone: (none — backlog)
  • Parent Epic: #5502

Subtasks

  • Add isinstance(conditions, list) guard before iterating node.config.get("conditions", []) and raise ValidationError if not a list
  • Add isinstance(route_to, str) guard before using route_to as a node ID and raise ValidationError if not a string
  • Remove unsafe str(cond["route_to"]) coercion; require route_to to already be a str
  • Write BDD scenarios covering: conditions is a string, conditions contains non-dict items, route_to is a complex object, and the valid happy path
  • Verify Pyright passes with no # type: ignore suppressions on the updated method
  • Update docstring for validate_references() to document the validation contract

Definition of Done

  • All three runtime crash scenarios raise ValidationError with clear, actionable messages
  • No str() coercion applied to route_to; explicit isinstance(route_to, str) guard in place
  • Pyright type-checks the updated method cleanly
  • BDD scenarios cover all crash scenarios and the happy path
  • All nox stages pass
  • Coverage >= 97%

Automated by CleverAgents Bot
Supervisor: Acting on behalf of: Bug Hunt Cycle 2 | Agent: new-issue-creator

## Background and Context `RouteDefinition.validate_references()` in `src/cleveragents/actor/schema.py` accesses `node.config` for conditional nodes without proper type guards. The code treats `node.config` as `dict[str, Any]` but iterates over its `"conditions"` key and accesses `"route_to"` on each element without validating that these values conform to the expected structure. When a malformed YAML actor definition is loaded, this produces cryptic `TypeError` exceptions instead of actionable `ValidationError` messages. ## Current Behavior ```python def validate_references(self) -> None: # Build adjacency list... for node in self.nodes: if node.type == NodeType.CONDITIONAL and isinstance(node.config, dict): for cond in node.config.get("conditions", []): # Assumes list if isinstance(cond, dict) and "route_to" in cond: target = str(cond["route_to"]) # Unsafe cast if target in node_ids: adj[node.id].append(target) ``` **Type safety issues:** 1. **Unvalidated list access** — `node.config.get("conditions", [])` assumes the result is iterable, but the value could be any type (e.g., a string `"not_a_list"`), causing `TypeError` on iteration. 2. **Unsafe string conversion** — `str(cond["route_to"])` silently converts complex objects (e.g., `{"nested": {"object": True}}`) to strings like `"{'nested': {'object': True}}"`, hiding type errors and producing nonsensical node IDs. 3. **No structure validation** — The code assumes conditional nodes have a specific config schema without validating it, violating the project's fail-fast and explicit-over-implicit principles. **Runtime crash scenarios:** ```yaml # Scenario 1: conditions is not a list nodes: - id: "bad_conditional" type: "conditional" config: conditions: "not_a_list" # TypeError on iteration # Scenario 2: condition items are not dicts nodes: - id: "bad_conditional" type: "conditional" config: conditions: ["string", 123, null] # TypeError accessing route_to # Scenario 3: route_to is complex object nodes: - id: "bad_conditional" type: "conditional" config: conditions: - route_to: {nested: {object: true}} # str() hides the issue ``` ## Expected Behavior - Malformed conditional node configs raise a clear `ValidationError` with an actionable message identifying the offending node and the structural problem. - `validate_references()` uses explicit type guards before accessing nested config values. - Complex objects in `route_to` are rejected with a descriptive error rather than silently coerced to strings. - All public method arguments are validated at entry per CONTRIBUTING.md error-handling guidelines. ## Acceptance Criteria - `validate_references()` validates that `node.config["conditions"]` is a `list` before iterating; raises `ValidationError` if not. - Each element in `conditions` is validated to be a `dict` with a `str`-typed `"route_to"` key; raises `ValidationError` for any violation. - No `str()` coercion is applied to `route_to`; the value must already be a `str` or a `ValidationError` is raised. - All three crash scenarios above produce `ValidationError` with clear, actionable messages. - Pyright type-checks the updated method without errors or `# type: ignore` suppressions. - BDD scenarios cover all three crash scenarios and the happy path. ## Supporting Information - **File**: `src/cleveragents/actor/schema.py` - **Method**: `RouteDefinition.validate_references()` - **Related**: CONTRIBUTING.md — "Argument Validation", "Fail-Fast Principles", "Type Safety" - **Spec**: Actor GRAPH conditional node config schema (docs/specification.md — Actor section) > **Backlog note:** This issue was discovered during autonomous operation > on milestone v3.2.0. It does not block milestone completion and has been > placed in the backlog for human review and future milestone assignment. ## Metadata - **Branch**: `bugfix/actor-route-definition-type-safety` - **Commit Message**: `fix(actor): add type guards to RouteDefinition.validate_references() for conditional node config` - **Milestone**: *(none — backlog)* - **Parent Epic**: #5502 ## Subtasks - [ ] Add `isinstance(conditions, list)` guard before iterating `node.config.get("conditions", [])` and raise `ValidationError` if not a list - [ ] Add `isinstance(route_to, str)` guard before using `route_to` as a node ID and raise `ValidationError` if not a string - [ ] Remove unsafe `str(cond["route_to"])` coercion; require `route_to` to already be a `str` - [ ] Write BDD scenarios covering: `conditions` is a string, `conditions` contains non-dict items, `route_to` is a complex object, and the valid happy path - [ ] Verify Pyright passes with no `# type: ignore` suppressions on the updated method - [ ] Update docstring for `validate_references()` to document the validation contract ## Definition of Done - [ ] All three runtime crash scenarios raise `ValidationError` with clear, actionable messages - [ ] No `str()` coercion applied to `route_to`; explicit `isinstance(route_to, str)` guard in place - [ ] Pyright type-checks the updated method cleanly - [ ] BDD scenarios cover all crash scenarios and the happy path - [ ] All nox stages pass - [ ] Coverage >= 97% --- **Automated by CleverAgents Bot** Supervisor: Acting on behalf of: Bug Hunt Cycle 2 | Agent: new-issue-creator
Author
Owner

Verified — Type safety bug: RouteDefinition validation assumes unvalidated config structure. MoSCoW: Should-have. Priority: Medium.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Type safety bug: RouteDefinition validation assumes unvalidated config structure. MoSCoW: Should-have. Priority: Medium. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
cleveragents/cleveragents-core#7157
No description provided.