You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: iOS app sends an image-only message without the attachment-only prompt the desktop adds #14049
I searched existing issues and did not find a duplicate.
I included enough detail to reproduce or investigate the problem.
Area
apps/mobile
Steps to reproduce
In the iOS app, attach an image and send it with no text.
Do the same in the desktop app.
Expected behavior
Both clients send the same turn. The desktop sends [User attached one or more files without additional text. Respond using the conversation context and the attached files.] as the text.
Actual behavior
The iOS app sends only the image reference, for example . The agent receives the image and the server's [Attached image "…" is saved at: …] line, but no instruction for an image-only turn.
Likely cause
ATTACHMENT_ONLY_BOOTSTRAP_PROMPT is defined in apps/web/src/components/chat/composerPromptHistory.ts and applied only by the web client (ChatView.tsx, sendQueuedMessage.ts). The iOS outbox sends the composer text unchanged through serializeComposerMessageForServer (apps/mobile/src/lib/composerContext.ts, called from use-thread-outbox-drain.ts).
Suggested fix
Move the constant to packages/shared, and in the iOS app use it when the text has nothing but attachment references.
Impact
Minor bug or occasional failure
Version or commit
iOS app 1.3.0 (85); desktop server 0.0.43-nightly.20260926.2282; main @ 94f92a7
Environment
iPhone, connected through the T3 Connect relay; provider Claude (Opus 5.5)
Confirmed on current main. This is a real client mismatch, and it is not fixed by a newer server. The desktop app is the web client. An image added to an empty composer stays on the shelf and is not inserted as a chip (imageAttachmentsGetChips in ChatComposer.tsx is false when the prompt has no prose). Send then substitutes an empty prompt with ATTACHMENT_ONLY_BOOTSTRAP_PROMPT in both ChatView.tsx and sendQueuedMessage.ts:
[User attached one or more files without additional text. Respond using the conversation context and the attached files.]
The mobile app always inserts an inline image reference (appendComposerDraftAttachments(..., { appendReference: true }) from photo pick and paste in use-thread-composer-state.ts, and the same option in the new-task composer). The outbox then sends queuedMessage.text unchanged from both drain paths in use-thread-outbox-drain.ts (serializeComposerMessageForServer around the existing-thread startTurn and the new-thread buildProjectThreadStartTurnInput). There is no bootstrap constant under apps/mobile.
The image is not dropped. The server still attaches the bytes and appends [Attached image "…" is saved at: …]. Because the text is , projectComposerContextForProvider also turns that into an [Image: …; ref=…] marker plus a t3_context envelope (name, mime type, size, attachment id). What the provider does not get is the instruction to respond. The same React Native path covers Android, not only iOS 1.3.0.
The suggested shared constant is right (packages/shared, so web prompt-recall can keep treating that sentence as non-user text). Do not substitute it for every attachment-only reference. Web still sends file-only messages as the file chip; only image-only messages with no prose become the bootstrap. Limit the mobile swap to text that is nothing but image references, and apply it on both outbox dispatch paths so the stored turn matches desktop. No existing issue covers this.
Activity
juliusmarminge commentedon Sep 28, 2026
Triage
Confirmed on current
main. This is a real client mismatch, and it is not fixed by a newer server. The desktop app is the web client. An image added to an empty composer stays on the shelf and is not inserted as a chip (imageAttachmentsGetChipsinChatComposer.tsxis false when the prompt has no prose). Send then substitutes an empty prompt withATTACHMENT_ONLY_BOOTSTRAP_PROMPTin bothChatView.tsxandsendQueuedMessage.ts:[User attached one or more files without additional text. Respond using the conversation context and the attached files.]The mobile app always inserts an inline image reference (
appendComposerDraftAttachments(..., { appendReference: true })from photo pick and paste inuse-thread-composer-state.ts, and the same option in the new-task composer). The outbox then sendsqueuedMessage.textunchanged from both drain paths inuse-thread-outbox-drain.ts(serializeComposerMessageForServeraround the existing-threadstartTurnand the new-threadbuildProjectThreadStartTurnInput). There is no bootstrap constant underapps/mobile.The image is not dropped. The server still attaches the bytes and appends
[Attached image "…" is saved at: …]. Because the text is,projectComposerContextForProvideralso turns that into an[Image: …; ref=…]marker plus at3_contextenvelope (name, mime type, size, attachment id). What the provider does not get is the instruction to respond. The same React Native path covers Android, not only iOS 1.3.0.The suggested shared constant is right (
packages/shared, so web prompt-recall can keep treating that sentence as non-user text). Do not substitute it for every attachment-only reference. Web still sends file-only messages as the file chip; only image-only messages with no prose become the bootstrap. Limit the mobile swap to text that is nothing but image references, and apply it on both outbox dispatch paths so the stored turn matches desktop. No existing issue covers this.