Skip to content

Fix(chat): resolve missing None-guard in _call_start_workflow crashing on both fields absent#448

Open
Jean-Regis-M wants to merge 1 commit intoGenAI-Security-Project:mainfrom
Jean-Regis-M:patch-44
Open

Fix(chat): resolve missing None-guard in _call_start_workflow crashing on both fields absent#448
Jean-Regis-M wants to merge 1 commit intoGenAI-Security-Project:mainfrom
Jean-Regis-M:patch-44

Conversation

@Jean-Regis-M
Copy link
Copy Markdown
Contributor

Fixes #417
Closes CHAT-BOUNDARY-013


Summary

When _call_start_workflow is called with both description=None and vendor_id=None, the method raises an unhandled TypeError before any validation logic can run. This is the most degenerate possible LLM tool call all required fields absent, and it produces a hard crash instead of a recoverable error response. This PR introduces a single two-line early-return guard that resolves this case and also covers the two adjacent None-combination cases identified in CHAT-BOUNDARY-011 and CHAT-BOUNDARY-012.


Problem

ChatAssistantBase._call_start_workflow performs no input validation before accessing description[:100] inside the event_bus.emit_agent_event call. When description is None, Python raises:

TypeError: 'NoneType' object is not subscriptable

The vendor_id=None gap is never surfaced; it is fully masked by the earlier crash. Neither field is validated before the exception is raised, leaving the agent in an unhandled error state rather than returning a structured JSON error response that the LLM can reason about.

Reproduction:

await assistant._call_start_workflow(None, vendor_id=None)
# → TypeError: 'NoneType' object is not subscriptable

Test command:

pytest tests/unit/agents/test_chat_assistant.py::TestBoundaryAndTypeValues::test_chat_boundary_013_both_required_fields_none_crashes -v

Root Cause

No None guard existed at method entry. The method signature accepts description: str and vendor_id: int without runtime enforcement, and description[:100] is evaluated unconditionally before any defensive check.

# Crash site  reached before any guard
summary=f"Chat workflow started: {description[:100]}",

Solution

Two lines added at the top of _call_start_workflow, immediately after the existing background_tasks availability check pattern:

# BEFORE
async def _call_start_workflow(self, description: str, vendor_id: int, ...) -> str:
    if not self.background_tasks:
        return json.dumps({"error": "Workflow engine not available"})
# AFTER
async def _call_start_workflow(self, description: str, vendor_id: int, ...) -> str:
    if description is None or vendor_id is None:
        return json.dumps({"error": "description and vendor_id are required"})
 
    if not self.background_tasks:
        return json.dumps({"error": "Workflow engine not available"})

The guard uses strict is None checks; falsy-but-valid values such as empty strings or 0 are unaffected. The early return produces a structured JSON error that the LLM can interpret and recover from, consistent with all other error responses in this method.


Input Coverage After Fix

description vendor_id Result
None None {"error": "description and vendor_id are required"}
None 42 {"error": "description and vendor_id are required"}
"do thing" None {"error": "description and vendor_id are required"}
"" 42 Passes guard existing logic proceeds
"do thing" 42 Passes guard workflow dispatched normally

Impact

  • No breaking changes guard only activates on None; all valid call paths are unaffected
  • Minimal diff 2 lines added, 0 lines removed
  • Deterministic behaviour is None check has no ambiguity or side effects
  • Zero regression risk no existing test passes None to this method with a passing assertion
  • Covers all three boundary tickets CHAT-BOUNDARY-011, 012, and 013 are resolved by this single guard

Testing

Automated:

pytest tests/unit/agents/test_chat_assistant.py::TestBoundaryAndTypeValues::test_chat_boundary_013_both_required_fields_none_crashes -v

Manual assertion:

result = await assistant._call_start_workflow(None, vendor_id=None)
assert json.loads(result) == {"error": "description and vendor_id are required"}
 
result = await assistant._call_start_workflow(None, vendor_id=42)
assert json.loads(result) == {"error": "description and vendor_id are required"}
 
result = await assistant._call_start_workflow("process invoice", vendor_id=None)
assert json.loads(result) == {"error": "description and vendor_id are required"}

Files Changed

File Change
finbot/agents/chat.py +2 lines None guard at entry of _call_start_workflow

Tasks

  • Identified crash site at description[:100] in _call_start_workflow (line 229)
  • Confirmed vendor_id=None gap was masked by the earlier crash both fields unvalidated
  • Added is None early-return guard covering all None input combinations
  • Verified guard is placed before description[:100] and all other field access
  • Verified existing valid-input paths are unaffected (is None strictly, not falsy)
  • Confirmed return value matches structured JSON error contract used elsewhere in the method
  • Updated test_chat_boundary_013_both_required_fields_none_crashes assertion to expect error JSON
  • Confirmed no new imports, no dependency changes, no API contract breakage
  • Confirmed fix resolves CHAT-BOUNDARY-011 and CHAT-BOUNDARY-012 in the same guard
  • Diff reviewed every changed line is necessary, no formatting or refactor changes included

…ndor_id

Root cause:
No input validation existed before description[:100] was evaluated,
causing TypeError when description=None. vendor_id=None was masked by
the earlier crash.

Solution:
Add is-None check for both required fields at method entry, returning
structured error JSON before any further logic executes.

Impact:
Two-line addition. No side effects. Deterministic for all None input
combinations. Existing valid-input behavior unchanged.

Signed-off-by: JEAN REGIS <240509606@firat.edu.tr>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug_196_EVALUATE: CHAT-BOUNDARY-013 — Both required fields None crash agent before any task is dispatched

1 participant