🌐 US-Proxy
class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
Skip to content

feat: surface model content-filter refusals as a blocked chat error - #27118

Merged
ethanndickson merged 2 commits into
mainfrom
ethan/codagt-611-surface-refusal-errors-instead-of-blank-thinking-state
Jul 15, 2026
Merged

feat: surface model content-filter refusals as a blocked chat error#27118
ethanndickson merged 2 commits into
mainfrom
ethan/codagt-611-surface-refusal-errors-instead-of-blank-thinking-state

Conversation

@ethanndickson

@ethanndickson ethanndickson commented Jul 9, 2026

Copy link
Copy Markdown
Member

Blocked turns from a provider's content filter (Anthropic's refusal stop reason with empty content) previously ended silently on the "Thinking" spinner. They now end as a terminal content_filter error that renders as a "Response blocked" message with the provider's category and explanation.

image

Closes CODAGT-611

Follow-ups will involve implementing fallbacks, but this alone is pretty important

@linear-code

linear-code Bot commented Jul 9, 2026

Copy link
Copy Markdown

CODAGT-611

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Docs preview

📖 View docs preview for docs/reference/api/chats.md

@ethanndickson
ethanndickson force-pushed the ethan/codagt-611-surface-refusal-errors-instead-of-blank-thinking-state branch 2 times, most recently from 81beeb8 to 6b41de7 Compare July 9, 2026 08:33
@ethanndickson
ethanndickson marked this pull request as ready for review July 9, 2026 08:33
@ethanndickson
ethanndickson requested a review from ibetitsmike July 9, 2026 08:40
@ethanndickson
ethanndickson force-pushed the ethan/codagt-611-surface-refusal-errors-instead-of-blank-thinking-state branch from 6b41de7 to 01cc89f Compare July 13, 2026 07:17
When a provider's real-time safety classifiers block a response
(e.g. Anthropic stop_reason "refusal"), the turn previously ended
silently: the fantasy SDK mapped the stop reason to unknown and the
empty-content finish looked like a normal stop, leaving the UI stuck
on "Thinking" with no output.

Bump the fantasy fork to pick up refusal -> FinishReasonContentFilter
mapping with stop_details carried as RefusalMetadata (fork change 15,
coder/fantasy#41). chatloop now turns an empty content-filter finish
into a terminal, non-retryable content_filter classified error, using
the provider's explanation as the message and the refusal category as
the detail, with a generic per-provider fallback message. The UI
renders it as "Response blocked".

Partial-content refusals (classifier blocks after some output) still
persist the partial text and end the turn normally; surfacing those is
a separate product decision.

Coder Agents generated.
@ethanndickson
ethanndickson force-pushed the ethan/codagt-611-surface-refusal-errors-instead-of-blank-thinking-state branch from 01cc89f to 69b79f3 Compare July 14, 2026 03:15
…entFilterError

Merge TestContentFilterMessage into TestContentFilterError as a
table-driven test. contentFilterError routes through ContentFilterMessage
for both the metadata and default-fallback paths, so asserting the
classified message covers the verbatim category, default, whitespace,
and unknown-provider cases without a separate test.
@ethanndickson
ethanndickson requested a review from mafredri July 14, 2026 14:36
@ethanndickson
ethanndickson merged commit cc11c8a into main Jul 15, 2026
31 of 33 checks passed
@ethanndickson
ethanndickson deleted the ethan/codagt-611-surface-refusal-errors-instead-of-blank-thinking-state branch July 15, 2026 06:36
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants