diff --git a/apps/mobile/src/components/agents/message-bubble.test.ts b/apps/mobile/src/components/agents/message-bubble.test.ts
index ece3b83d92..4cd18985bd 100644
--- a/apps/mobile/src/components/agents/message-bubble.test.ts
+++ b/apps/mobile/src/components/agents/message-bubble.test.ts
@@ -303,6 +303,26 @@ describe('MessageBubble failure footer', () => {
expect(findElementByType(tree, 'Button', p => p.accessibilityLabel === 'Retry')).toBeNull();
});
+ it('states a generic assistant failure once, without a detail line repeating the title', async () => {
+ const tree = await renderBubbleWithHandlers(
+ assistantMessageWithError('m-asst-laconic', 'APIError'),
+ {
+ onRetryMessage: vi.fn<(message: StoredMessage) => void>(),
+ }
+ );
+ expect(findText(tree, t => t === 'Response failed')).toBe(true);
+ expect(findText(tree, t => t === 'The response failed.')).toBe(false);
+ });
+
+ it('keeps the classified detail line for a known assistant error', async () => {
+ const tree = await renderBubbleWithHandlers(
+ assistantMessageWithError('m-asst-known', 'ProviderAuthError'),
+ { onRetryMessage: vi.fn<(message: StoredMessage) => void>() }
+ );
+ expect(findText(tree, t => t === 'Response failed')).toBe(true);
+ expect(findText(tree, t => t === 'The provider rejected the request.')).toBe(true);
+ });
+
it('does not render the footer when no handler is supplied', async () => {
const tree = await renderBubbleWithHandlers(userMessage('m-nohandler'), {
deliveryState: { status: 'failed', error: 'nope', reason: 'exhausted' },
diff --git a/apps/mobile/src/components/agents/message-bubble.tsx b/apps/mobile/src/components/agents/message-bubble.tsx
index 081a00c041..b8dcef0b74 100644
--- a/apps/mobile/src/components/agents/message-bubble.tsx
+++ b/apps/mobile/src/components/agents/message-bubble.tsx
@@ -162,7 +162,9 @@ function MessageBubbleImpl({
>
{failure.title}
- {failure.detail}
+ {failure.detail !== null ? (
+ {failure.detail}
+ ) : null}
{failure.canRetry && onRetryMessage ? (