Skip to content

Fix coroutine warning in JSONEncoder#4128

Open
nishkarshs1 wants to merge 2 commits into
traceloop:mainfrom
ns1-oss:fix-async-json-warning
Open

Fix coroutine warning in JSONEncoder#4128
nishkarshs1 wants to merge 2 commits into
traceloop:mainfrom
ns1-oss:fix-async-json-warning

Conversation

@nishkarshs1
Copy link
Copy Markdown

@nishkarshs1 nishkarshs1 commented May 12, 2026

Fixes #3967

What changed

This updates JSONEncoder to handle objects with async json() methods more safely.

Previously, the encoder checked inspect.iscoroutinefunction(json_method) before calling .json(). In the case of starlette.requests.Request, this check did not reliably detect the async method, which caused the coroutine to be created without being awaited and produced:

RuntimeWarning: coroutine 'Request.json' was never awaited

The updated logic checks whether the returned value is a coroutine after calling the method and closes it safely before falling back to the class name.

Verification

Tested locally using a FastAPI endpoint decorated with @workflow and passing a Request object. The warning no longer appears.

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced JSON serialization robustness by improving how custom serialization methods and asynchronous operations are handled internally.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

📝 Walkthrough

Walkthrough

The JSONEncoder.default method is refactored to detect and safely close coroutines returned by async json() methods. Instead of guarding calls with inspect.iscoroutinefunction(), the code now invokes json() and checks the result with inspect.iscoroutine(), then closes any coroutine found. This prevents RuntimeWarning: coroutine was never awaited when serializing objects like FastAPI Request with async methods.

Changes

JSONEncoder coroutine safety

Layer / File(s) Summary
JSONEncoder.default coroutine detection and cleanup
packages/traceloop-sdk/traceloop/sdk/utils/json_encoder.py
Lookup of to_json and json methods switches to getattr + callable() checks; json() call wrapped in try/except; post-call inspect.iscoroutine(result) detects and closes unawaited coroutines, falling back to class name serialization; fallback to __class__.__name__ or super().default() unchanged.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit hops through async streams so bright,
Where coroutines once wandered through the night,
Now caught and closed before they float away,
JSON encodes them safely, come what may! 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: fixing a coroutine warning in JSONEncoder to handle async json() methods properly.
Linked Issues check ✅ Passed The code changes directly address issue #3967 by modifying JSONEncoder to safely handle async json() methods by checking if the result is a coroutine after invocation and closing it.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the JSONEncoder to handle async json() methods, with no unrelated modifications present in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/traceloop-sdk/traceloop/sdk/utils/json_encoder.py (1)

21-34: ⚡ Quick win

Consider an iscoroutinefunction fast-path before invoking json_method().

The current "call then inspect" pattern correctly fixes the reported warning, but it always invokes the callable — even when we can cheaply determine ahead of time that it's async. For objects whose json callable has side effects on invocation (resource acquisition, logging, state mutation in __call__), this is unnecessary work. Checking inspect.iscoroutinefunction(json_method) first as a fast path, and keeping the post-call inspect.iscoroutine(result) check as a fallback, gives defense-in-depth: the fast path covers the common case, and the post-call check still catches wrapped/decorated callables that iscoroutinefunction misses (the case this PR explicitly motivates).

♻️ Proposed defense-in-depth refactor
         if callable(json_method):
+            if inspect.iscoroutinefunction(json_method):
+                return o.__class__.__name__
             try:
                 result = json_method()

                 if inspect.iscoroutine(result):
                     result.close()
                     return o.__class__.__name__

                 return result

             except Exception:
                 pass
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/traceloop-sdk/traceloop/sdk/utils/json_encoder.py` around lines 21 -
34, Add a cheap async fast-path before invoking the object's json callable: in
json_encoder.py, when you obtain json_method (the attribute on the object),
first check inspect.iscoroutinefunction(json_method) and if True, avoid calling
it and return o.__class__.__name__; keep the existing try/except and the
post-call inspect.iscoroutine(result) fallback intact so decorated/wrapped async
callables are still detected after invocation (referencing json_method, result,
and the existing exception handling).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/traceloop-sdk/traceloop/sdk/utils/json_encoder.py`:
- Line 39: The file packages/traceloop-sdk/traceloop/sdk/utils/json_encoder.py
is missing a trailing newline (Ruff W292); add a single newline character at EOF
so the file ends with a newline, e.g., after the final line containing "return
super().default(o)" in the default(self, o) implementation of your JSONEncoder
subclass.

---

Nitpick comments:
In `@packages/traceloop-sdk/traceloop/sdk/utils/json_encoder.py`:
- Around line 21-34: Add a cheap async fast-path before invoking the object's
json callable: in json_encoder.py, when you obtain json_method (the attribute on
the object), first check inspect.iscoroutinefunction(json_method) and if True,
avoid calling it and return o.__class__.__name__; keep the existing try/except
and the post-call inspect.iscoroutine(result) fallback intact so
decorated/wrapped async callables are still detected after invocation
(referencing json_method, result, and the existing exception handling).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8c8c43ca-7b5c-4aaf-b24b-9a63281d4ba8

📥 Commits

Reviewing files that changed from the base of the PR and between 6d3e696 and 9f51f1c.

📒 Files selected for processing (1)
  • packages/traceloop-sdk/traceloop/sdk/utils/json_encoder.py

return o.__class__.__name__

return super().default(o)
return super().default(o) No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a trailing newline at end of file.

Ruff (W292) flags the file as missing a final newline.

📝 Proposed fix
-        return super().default(o)
+        return super().default(o)
+
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return super().default(o)
return super().default(o)
🧰 Tools
🪛 Ruff (0.15.12)

[warning] 39-39: No newline at end of file

Add trailing newline

(W292)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/traceloop-sdk/traceloop/sdk/utils/json_encoder.py` at line 39, The
file packages/traceloop-sdk/traceloop/sdk/utils/json_encoder.py is missing a
trailing newline (Ruff W292); add a single newline character at EOF so the file
ends with a newline, e.g., after the final line containing "return
super().default(o)" in the default(self, o) implementation of your JSONEncoder
subclass.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 12, 2026

CLA assistant check
All committers have signed the CLA.

@nishkarshs1
Copy link
Copy Markdown
Author

@cla-bot recheck

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants