Fix task configuration with None context parameter in YAML

Fixes #3696

When context: None is specified in YAML, yaml.safe_load() converts it to
the string 'None' instead of Python's None object. The code was attempting
to iterate over this string character by character, causing KeyError: 'N'
when trying to look up task names.

Changes:
- Added isinstance(context_list, list) check in crew_base.py before
  iterating context_list to handle YAML's conversion of None to string
- Added test case test_task_with_none_context_from_yaml to verify tasks
  can be configured with context: None without errors
- Added test YAML configurations in tests/config_none_context/ to
  reproduce and verify the fix

The fix ensures that only actual list values are processed, allowing
None and other non-list values to pass through without causing errors.

Co-Authored-By: João <joao@crewai.com>
This commit is contained in:
Devin AI
2025-10-11 06:34:20 +00:00
parent 7b550ebfe8
commit d0e629ee8d
4 changed files with 55 additions and 3 deletions

View File

@@ -260,9 +260,10 @@ def CrewBase(cls: T) -> T: # noqa: N802
output_pydantic_functions: dict[str, Callable],
) -> None:
if context_list := task_info.get("context"):
self.tasks_config[task_name]["context"] = [
tasks[context_task_name]() for context_task_name in context_list
]
if isinstance(context_list, list):
self.tasks_config[task_name]["context"] = [
tasks[context_task_name]() for context_task_name in context_list
]
if tools := task_info.get("tools"):
self.tasks_config[task_name]["tools"] = [

View File

@@ -0,0 +1,5 @@
test_agent:
role: Test Agent
goal: Test goal
backstory: Test backstory
verbose: true

View File

@@ -0,0 +1,11 @@
task_with_none_context:
description: A test task with None context
expected_output: Some output
agent: test_agent
context: None
task_with_valid_context:
description: A test task with valid context
expected_output: Some output
agent: test_agent
context: [task_with_none_context]

View File

@@ -287,3 +287,38 @@ def test_internal_crew_with_mcp():
adapter_mock.assert_called_once_with(
{"host": "localhost", "port": 8000}, connect_timeout=120
)
def test_task_with_none_context_from_yaml():
@CrewBase
class CrewWithNoneContext:
agents_config = "config_none_context/agents.yaml"
tasks_config = "config_none_context/tasks.yaml"
agents: list[BaseAgent]
tasks: list[Task]
@agent
def test_agent(self):
return Agent(config=self.agents_config["test_agent"])
@task
def task_with_none_context(self):
return Task(config=self.tasks_config["task_with_none_context"])
@task
def task_with_valid_context(self):
return Task(config=self.tasks_config["task_with_valid_context"])
@crew
def crew(self):
return Crew(agents=self.agents, tasks=self.tasks, verbose=True)
crew_instance = CrewWithNoneContext()
task_none = crew_instance.task_with_none_context()
assert task_none is not None
task_valid = crew_instance.task_with_valid_context()
assert task_valid.context is not None
assert len(task_valid.context) == 1