Skip to content

fix(agent): treat file-only messages as empty conversation to prevent EMPTY_COMPLETION - #1466

Closed
kwakayama wants to merge 1 commit into
mainfrom
fix/file-only-empty-conversation
Closed

kwakayama wants to merge 1 commit into
mainfrom
fix/file-only-empty-conversation

Conversation

@kwakayama

Copy link
Copy Markdown
Contributor

Problem

When a user sends a chat message with only file attachments and no text, isEmptyConversation returned false because non-text parts short-circuited the every() check. After conversion via agent-runtime-message-adapter, the LLM received only a file annotation text with no user intent:

Attached files from earlier conversation context:

<uploaded_files>
<file name="screenshot.png" ... />
</uploaded_files>

With no actionable request, some LLMs return an empty response. This triggers shouldFailEmptyFinalizedMessage in the agent runtime, producing an EMPTY_COMPLETION error — which is the root cause of veryfront/veryfront-studio#3499.

Fix

Change the non-text part check in isEmptyConversation from return false to return true.

A message composed entirely of file parts has no text intent — it should be treated the same as an empty text message. The configured emptyConversationPrompt (or DEFAULT_EMPTY_CONVERSATION_PROMPT) is used instead, so the LLM always receives actionable text.

Mixed messages (text + files) are unaffected: a non-empty text part still returns false from parts.every(), leaving the existing conversion path untouched.

Evidence

TDD cycle in src/agent/runtime-message-preparation.test.ts:

  • Two new tests added: file-only and whitespace-text-plus-file cases
  • Both tests failed against the old return false code
  • Both tests pass after the one-line fix
ok | 4 passed | 0 failed (11ms)

Related

  • Studio fix (error panel now shows for EMPTY_COMPLETION): veryfront/veryfront-studio#3504
  • Agent fix (propagate URL resolution errors): veryfront/veryfront-agent (separate PR)

… EMPTY_COMPLETION

When a user sends only file attachments with no text, isEmptyConversation
previously returned false (file parts were treated as non-empty). After
conversion, the LLM received only a file annotation with no user intent
text, which could result in an empty response and trigger EMPTY_COMPLETION.

Change the non-text part check from `return false` to `return true` so
that a message composed entirely of file parts (with no meaningful text)
is treated as an empty conversation. The configured default prompt is used
instead, ensuring the LLM always receives actionable text.

Mixed messages (text + files) are unaffected because a non-empty text part
returns false from parts.every(), keeping the existing conversion path.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8571b12151

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

return !part.text || part.text.trim() === "";
}
return false;
return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit empty-conversation shortcut to file parts

This change makes isEmptyConversation treat every non-text part as ignorable, not just file parts. In a single user message that contains structured content like data-* or tool-related parts (both valid ChatUiMessagePart variants), parts.every(...) now returns true and the function replaces the message with emptyConversationPrompt, dropping the original payload before prepareProviderModelMessagesFromUiMessages can convert it. That creates a behavioral regression for first-turn structured user inputs; the non-text branch should be narrowed to file-only (or another explicitly allowed subset) instead of all non-text parts.

Useful? React with 👍 / 👎.

@kwakayama

Copy link
Copy Markdown
Contributor Author

Closing — this is the wrong fix.

The isEmptyConversation change treats a file-only message as "nothing was said" and replaces it with the generic suggestions prompt, silently discarding the user's attachments. That's wrong semantics and bad UX.

Root cause (deeper investigation)

The actual problem is in agent-runtime-message-adapter.ts:130-132, which strips every image/file part and replaces them with a text annotation. For a file-only message with no text, the LLM receives only "Attached files from earlier conversation context: ..." with no user intent — which can trigger EMPTY_COMPLETION.

Correct architectural direction

Files in this system travel out-of-band (uploaded separately, referenced by uploadId/URL). The AG-UI wire contract (AgUiRuntimeUserMessageSchema) enforces content: string — multimodal parts are not part of the inbound AG-UI spec. The annotation approach is correct for the protocol boundary.

The right fix is either:

  1. Make the annotation context-sensitive — when there is no user text alongside the files, emit a different preamble so the LLM knows the user expects a response about the files
  2. Add UI-layer validation in veryfront-studio to require text when attachments are submitted (prevent the ambiguous state at the source)

Neither requires changing isEmptyConversation or the AgentRuntimeMessagePart schema.

@kwakayama kwakayama closed this May 7, 2026
@ariskemper
ariskemper deleted the fix/file-only-empty-conversation branch May 13, 2026 10:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant