moeru-ai/airi · warning · Error

Retry target has no retriable source message

Error message

Retry target has no retriable source message

What it means

retry() maps the target message to the user message that produced the turn using retrySourceIndexFrom; a negative result means no retriable source message exists for the requested index. Concretely, there is no user-role message at or before that position (or the index is out of bounds), so there is nothing to re-execute — e.g. retrying the opening greeting.

Source

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

  async function send(payload: ChatSendPayload): Promise<ChatSendResult> {
    try {
      return await executeSend(payload)
    }
    catch (error) {
      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

View on GitHub (pinned to 677329427f)

Solutions

  1. Only render retry affordances on assistant turns that have a preceding user message.
  2. Prefer addressing the retry target by stable message id rather than array index.
  3. Refresh messages and recompute indices immediately before retrying.
  4. If it still fires, treat it as a no-op in the UI instead of an error toast.

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 hasUserSource = messages.slice(0, index + 1).some(m => m.role === 'user')
  if (!hasUserSource)
    return // nothing retriable (e.g. greeting or error stub)
  await chatStore.retry({ sessionId, index })
}
Defensive patterns

Strategy: validation

Validate before calling

const messages = chatSession.getSessionMessages(sessionId)
const hasRetriableSource = messages
  .slice(0, payload.index + 1)
  .some(message => message.role === 'user' && message.content?.trim())
if (!hasRetriableSource)
  return // nothing to retry (greeting, error stub, or shifted index)
await chatStore.retry(payload)

Type guard

function isRetriableMessageIndex(messages: ChatHistoryItem[], index: number): boolean {
  if (index < 0 || index >= messages.length) return false
  return messages.slice(0, index + 1).some(m => m.role === 'user' && Boolean(m.content?.trim()))
}

Try / catch

try {
  await chatStore.retry({ sessionId, index })
}
catch (error) {
  if (errorMessageFrom(error) === 'Retry target has no retriable source message')
    return // benign UI misuse: ignore
  throw error
}

Prevention

When it happens

Trigger: Retrying the first assistant greeting (no preceding user message); retrying an 'error' role message appended by appendSendError; index-based retry after messages were deleted so indices shifted; passing an index past the end of the message list.

Common situations: Retry UI rendered on every message including greetings and error stubs; long histories where the user retries after manually deleting earlier turns; component state holding an old index after the list re-rendered.

Related errors


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