[BUG] Empty structured task output files fall back to raw text
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 58.8k
- Forks
- 8.5k
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 109
Description
Description
When a task configured with output_json successfully converts its result to an empty object, TaskOutput.json_dict is {} but the output file contains the raw model text instead of {}.
Both sync and native-async execution select the file content with a truthiness check. That treats a valid empty dictionary the same as None (no structured result) and falls back to raw.
This report was prepared with AI assistance. I cannot apply repository labels as an external contributor; please add the required
llm-generatedlabel.
Steps to reproduce
- Create a task with
output_jsonandoutput_file. - Let structured conversion produce
{}while the raw response is non-JSON text. - Execute the task.
- Observe that
task.output.json_dict == {}, but the file contains the raw response.
Minimal equivalent of the affected selection:
json_dict = {}
raw = "raw fallback"
content = json_dict if json_dict else raw
assert content == "raw fallback"
Expected behavior
A present structured result should be authoritative even when empty. The output file should contain {}. Only json_dict is None should fall back to Pydantic or raw output.
Screenshots/Code snippets
The same condition exists in Task._execute_core() and Task._aexecute_core():
content = (
task_output.json_dict
if task_output.json_dict
else task_output.raw
)
Operating System
macOS Sonoma
Python Version
3.13 (the repository currently supports it; the issue template does not list it)
crewAI Version
Current main (3831e8b6c86f)
crewAI Tools Version
Not applicable
Virtual Environment
Venv (uv)
Evidence
A deterministic sync/async regression test produces TaskOutput(json_dict={}). On current main, both paths write raw fallback; changing the presence check to is not None makes both write valid {} JSON.
Possible Solution
Use task_output.json_dict is not None in both output-file selection paths and add sync/async behavior tests that read the actual written file.
Additional context
No open issue or pull request matching task_output.json_dict, _save_file, or empty structured task output was found before filing.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at Task._execute_core() and Task._aexecute_core(), where output-file content is selected, then review the deterministic sync/async regression coverage described in the issue. Done means both execution paths write {} when json_dict is an empty dictionary and still fall back only when no structured result is present.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 84/100