Skip to content

fix(types): distinguish a legitimate None result from never-set in streaming#6534

Open
chuenchen309 wants to merge 1 commit into
crewAIInc:mainfrom
chuenchen309:fix/streaming-result-none-sentinel
Open

fix(types): distinguish a legitimate None result from never-set in streaming#6534
chuenchen309 wants to merge 1 commit into
crewAIInc:mainfrom
chuenchen309:fix/streaming-result-none-sentinel

Conversation

@chuenchen309

Copy link
Copy Markdown

AI-generated contribution disclosure

I used an AI coding assistant (Claude) to help identify this bug and draft the fix. I reviewed the diff, wrote/verified the regression test in both red and green states, traced every other read site of the affected field to check for related issues, and ran the full local test suite + ruff + mypy before opening this PR. Per CONTRIBUTING.md I understand this should carry the llm-generated label — as an external contributor I don't have permission to apply labels myself, so flagging it here explicitly; happy for a maintainer to add it.

Problem

StreamingOutputBase (the base class behind FlowStreamingOutput/CrewStreamingOutput) initializes self._result to None and treats None as "no result was ever set":

if self._result is None:
    raise RuntimeError("No result available")

This misfires whenever the actual streamed result legitimately is None — e.g. a Flow whose final method has no return value — even though streaming completed successfully with no error.

The sibling StreamSessionBase class in the same file already solves exactly this problem correctly, using a module-level _MISSING = object() sentinel instead of comparing to None.

Fix

Apply the same _MISSING sentinel pattern to StreamingOutputBase. While tracing every read site of self._result in the file to make sure I wasn't missing a related spot, I found one more: CrewStreamingOutput.results' fallback branch (if self._result is not None: return [self._result]) made the same None-means-unset assumption and needed the same fix, or it would have silently broken (returning [_MISSING]) once the default changed.

Testing

  • Added test_result_can_legitimately_be_none to TestFlowStreamingOutput in tests/test_streaming.py, confirming .result returns None (rather than raising) after _set_result(None). Verified it fails against the pre-fix code and passes after.
  • Ran the full tests/test_streaming.py suite (38 passed).
  • ruff check/ruff format --check clean; mypy clean on the changed file.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa3744cb-431f-4c47-bbb9-0224f1547bc3

📥 Commits

Reviewing files that changed from the base of the PR and between e460c4a and 345b198.

📒 Files selected for processing (2)
  • lib/crewai/src/crewai/types/streaming.py
  • lib/crewai/tests/test_streaming.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/crewai/src/crewai/types/streaming.py
  • lib/crewai/tests/test_streaming.py

📝 Walkthrough

Walkthrough

Streaming outputs now distinguish an unset result from a completed result set to None, and tests cover accessing a legitimate None flow result after streaming.

Changes

Streaming result state

Layer / File(s) Summary
Sentinel-based result handling
lib/crewai/src/crewai/types/streaming.py, lib/crewai/tests/test_streaming.py
StreamingOutputBase uses _MISSING for unset results, while result access and CrewStreamingOutput.results recognize explicitly set None values. A flow streaming test verifies that behavior.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main fix: distinguishing a real None streaming result from an unset result.
Description check ✅ Passed The description accurately explains the bug, fix, and regression test and matches the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…reaming

StreamingOutputBase (base for FlowStreamingOutput/CrewStreamingOutput)
initialized self._result to None and treated None as "not set" when
raising RuntimeError("No result available"). This misfires whenever the
actual result legitimately is None -- e.g. a Flow whose final method
returns nothing -- even though streaming completed successfully with no
error.

The sibling StreamSessionBase class in the same file already solves this
correctly with a module-level _MISSING sentinel. Apply the same pattern
to StreamingOutputBase, and fix the one other call site that assumed
None-means-unset (CrewStreamingOutput.results' fallback to self._result).

Added a regression test confirming FlowStreamingOutput.result returns
None (rather than raising) when _set_result(None) was called. Confirmed
it fails against the pre-fix code and passes after. Ran the full
test_streaming.py suite (38 passed), ruff check/format, and mypy (clean).

Disclosure: I used an AI coding assistant (Claude) to help identify this
bug and draft the fix. I independently traced every other read site of
self._result in this file to catch a second place (CrewStreamingOutput
.results) that also assumed None-means-unset and would have silently
broken had I only patched the reported location.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chuenchen309
chuenchen309 force-pushed the fix/streaming-result-none-sentinel branch from e460c4a to 345b198 Compare July 15, 2026 06:19
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.

1 participant