moeru-ai/airi · warning · Error

Retry target has no retriable user message

Error message

Retry target has no retriable user message

What it means

retry() found the source message via retrySourceIndexFrom, but retryTextFrom(sourceMessage) could not extract any user text from it, so there is no prompt to re-send. This happens when the located user message has empty or missing text content — for example an attachment-only turn with no caption, or a corrupted/incomplete history record.

Source

Thrown at packages/stage-ui/src/stores/chat.ts:420

      appendSendError(payload.sessionId, error)
      throw error
    }
  }

  /** Replaces one stored turn with a new execution of its user message. */
  async function retry(payload: ChatRetryPayload): Promise<ChatSendResult> {
    if (!await chatSession.loadSession(payload.sessionId))
      throw new Error('Failed to load the target chat session')

    const currentMessages = chatSession.getSessionMessages(payload.sessionId)
    const sourceIndex = retrySourceIndexFrom(currentMessages, payload.index)
    if (sourceIndex < 0)
      throw new Error('Retry target has no retriable source message')

    const sourceMessage = currentMessages[sourceIndex]
    const text = retryTextFrom(sourceMessage)
    if (!text)
      throw new Error('Retry target has no retriable user message')

    chatSession.setSessionMessages(payload.sessionId, currentMessages.slice(0, sourceIndex))

    try {
      return await executeSend({
        sessionId: payload.sessionId,
        text,
        tools: payload.tools ?? sourceMessage?.tools,
      })
    }
    catch (error) {
      appendSendError(payload.sessionId, error)
      throw error
    }
  }

  /** Runs one stored tool call again and replaces its stored result. */
  async function rerunToolCall(payload: ChatToolCallRerunPayload): Promise<void> {

View on GitHub (pinned to 677329427f)

Solutions

  1. Disable retry for user turns whose text is empty (attachment-only messages).
  2. If the turn should have text, inspect the stored session record in IndexedDB and repair or drop the broken turn.
  3. Re-send the intended prompt as a new message instead of retrying.
  4. When migrating history formats, normalize user messages so text is present (or filter them out of retriable targets).

Example fix

// before
async function onRetry(index: number) {
  await chatStore.retry({ sessionId, index })
}

// after
async function onRetry(index: number) {
  const messages = chatSession.getSessionMessages(sessionId)
  const source = messages.slice(0, index + 1).reverse().find(m => m.role === 'user')
  if (!source?.content?.trim()) {
    toast.info('This turn has no text to retry')
    return
  }
  await chatStore.retry({ sessionId, index })
}
Defensive patterns

Strategy: validation

Validate before calling

const messages = chatSession.getSessionMessages(sessionId)
const source = messages.slice(0, payload.index + 1).reverse().find(m => m.role === 'user')
if (!source?.content?.trim()) {
  toast.info('This turn has no text to retry')
  return
}
await chatStore.retry(payload)

Type guard

function hasRetriableUserText(messages: ChatHistoryItem[], index: number): boolean {
  const source = messages.slice(0, index + 1).reverse().find(m => m.role === 'user')
  return Boolean(source?.content?.trim())
}

Try / catch

try {
  await chatStore.retry({ sessionId, index })
}
catch (error) {
  const message = errorMessageFrom(error) ?? ''
  if (message === 'Retry target has no retriable user message') {
    toast.info('Cannot retry a turn without text (e.g. attachments only)')
    return
  }
  throw error
}

Prevention

When it happens

Trigger: Retrying a user turn that consisted only of attachments (images/audio) with no text; a history record whose content field is empty due to an interrupted write or partial restore; message objects from an older schema where text lives under a different key.

Common situations: Users sending media-only messages then tapping retry; migrated or hand-edited chat histories with empty user turns; older record formats after a schema change in the session store.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/b9fa24dabbc6b09c. Report an issue: GitHub.