🪓 fix: throw on PII block instead of custom SSE event

The pii_blocked event needed a custom frontend handler that didn't
exist. Simpler: throw a friendly Error after processStream resolves
when run.getHaltReason() flags message_pii_filter_block. The
existing sendCompletion catch block already pushes a
ContentTypes.ERROR part into the assistant turn, so the user sees
a real error message in chat with no new client code.

Error body names the matched pattern labels so the user knows what
was caught (e.g. "Message blocked by PII filter: Anthropic API key.
Edit and retry.").

Warn mode still emits the pii_matches SSE event; that one still
needs a client toast handler (the frontend agent is wiring it).
This commit is contained in:
Dustin Healy 2026-06-07 17:51:48 -07:00
parent 860237da78
commit 940194d773

View file

@ -1106,27 +1106,25 @@ class AgentClient extends BaseClient {
config.signal = null;
if (
piiFilterResult != null &&
this.options.res != null &&
!this.options.res.writableEnded
) {
if (piiFilterResult != null) {
const piiHaltReason = run.getHaltReason?.();
if (piiHaltReason === 'message_pii_filter_block') {
// Surface a friendly error on block mode so the frontend
// doesn't render an empty assistant bubble. The existing
// error event vocabulary is whatever the host expects; use
// a typed payload so a downstream toast/banner can render
// a real message rather than the silent halt LibreChat
// would otherwise show.
sendEvent(this.options.res, {
type: 'pii_blocked',
reason: piiHaltReason,
matches: piiFilterResult.collector.matches,
});
} else if (
// Throwing here routes through the existing error UX in the
// sendCompletion catch block, which pushes a ContentTypes.ERROR
// part into the assistant turn. The user sees a real error
// message in chat instead of an empty bubble + stalled
// spinner. Labels are joined so the message names what was
// matched (e.g. "Anthropic API key, GitHub token").
const labels = piiFilterResult.collector.matches.map((m) => m.patternLabel).join(', ');
throw new Error(
`Message blocked by PII filter${labels.length > 0 ? `: ${labels}` : ''}. Edit and retry.`,
);
}
if (
appConfig?.messagePiiFilter?.onMatch === 'warn' &&
piiFilterResult.collector.matches.length > 0
piiFilterResult.collector.matches.length > 0 &&
this.options.res != null &&
!this.options.res.writableEnded
) {
sendEvent(this.options.res, {
type: 'pii_matches',