Repository navigation
fix(gateway): ignore tool-output blocks whose media type is not a string - #1871
Open
JasonBuildAI wants to merge 1 commit into
Open
JasonBuildAI wants to merge 1 commit into
JasonBuildAI wants to merge 1 commit into
Conversation
JasonBuildAI
force-pushed
the
fix/tool-media-nonstr-type
branch
from
October 10, 2026 06:18
094cdee to
696161d
Compare
A tool result can carry JSON where a block's "type" is an object or list. Membership tests against _MEDIA_BLOCK_TYPES then raise TypeError: unhashable type: 'dict', which 500s the thread history endpoint and can also break live tool-result enrichment and IM media delivery. Route every membership test through a helper that only accepts string types. History preview rewriting also keeps the raw tool output and logs a warning when it fails for any other reason (e.g. a media URL that cannot be quoted), so cosmetic enrichment can no longer fail the whole history page. Fixes TencentCloud#1869
JasonBuildAI
force-pushed
the
fix/tool-media-nonstr-type
branch
from
October 15, 2026 17:38
696161d to
32322ea
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GET /api/agents/{agent_id}/threads/{thread_id}/historyreturns 500 when a tool output in the thread carries a JSON block whosetypeis not a string, e.g.[{"type": {"kind": "image"}}, {"text": "plain"}](Fixes #1869).tool_media.pytests blocktypevalues for membership in_MEDIA_BLOCK_TYPES(afrozenset). Adictorlistvalue is unhashable, so the test itself raises:Media preview rewriting is presentational, but the exception escapes
_enrich_history_tool_media, so one bad row makes the whole thread unreadable in the dashboard. Two of the four sites are on live paths, so the same payload can also break streaming tool-result enrichment (_enrich_block_with_backend) and IM media delivery (block_to_content_part).Target branch
develop(feature / fix - default)main(release/*orhotfix/*only)Type of change
How it is fixed
_media_block_type(block): returnsblock["type"]only when it is a string present in_MEDIA_BLOCK_TYPES, otherwise"".iter_media_blocks,enrich_media_block_preview,_enrich_block_with_backend,block_to_content_part) with that helper, so a non-media block is skipped instead of raising._enrich_history_tool_medianow falls back to the raw tool output and logs a warning when preview rewriting fails (the issue's second suggestion). That guard is not hypothetical: even with the membership test fixed, a media block whose URL holds a lone surrogate (file:///tmp/\ud800.png) still raisesUnicodeEncodeErrorfromurllib.parse.quoteon the very same request path, so the endpoint stays 500-prone without it.Test plan
Both failure modes reproduced first, then re-checked after the change.
type(the reported bug):New tests (all failing on the unmodified tree, passing after):
uv run pytest tests/unit/gateway/test_tool_media.py tests/unit/api/test_chat_polish.py -q->71 passedtest_media_blocks_ignore_non_string_type(object / list / int / nulltype) coversiter_media_blocks+enrich_media_block_previewtest_enrich_sync_tolerates_non_string_block_typecovers the history preview string pathtest_enrich_tool_result_tolerates_non_string_block_typecovers live streaming enrichmenttest_block_to_content_part_ignores_non_string_typecovers IM media deliverytest_load_projected_tolerates_non_string_tool_block_typeandtest_load_projected_survives_unencodable_media_urlgo through_load_projected_thread_messages, i.e. exactly the request path that 500sShip bar - this clone has no
makeon Windows, so the Makefile targets were run as their underlying commands:uv run ruff check src tests->All checks passed!uv run ruff format --check src tests->1168 files already formatteduv run mypy src/octop->Success: no issues found in 565 source filesuv run pytest -n auto -q tests/unit/api tests/unit/gateway->767 passed, 12 skippeduv run pytest -n auto -m "not live" -q->51 failed, 4333 passed, 157 skippedThose 51 failures are pre-existing on
developin this environment, not caused by the change: a clean baseline run of the identical command with the change stashed gives the same failure set (52 failed, 4324 passed, 157 skipped), and the only delta istests/unit/gateway/test_versioned_history.py::test_process_exit_after_commit_keeps_last_fragment, which fails under xdist in the baseline run, passes with the change, and also passes standalone here. Most of them areUnicodeDecodeErrorfrom tests reading files / subprocess output without an explicit encoding on this GBK-locale Windows host.Checklist
make allpasses locally (commands above;makeis unavailable in this Windows clone)CHANGELOG.mdAI disclosure: this change was drafted with AI assistance at my request, under my direction and review.