666ghj/MiroFish · error · Error

res.error || t('step5.requestFailed')

Error message

res.error || t('step5.requestFailed')

What it means

Thrown by sendToReportAgent in Step5Interaction.vue when the backend call to chatWithReport (POST with simulation_id, message, chat_history) resolves with success=false or an empty data payload. This is the app's own request-wrapper convention: any non-2xx or business failure comes back as { success: false, error }, and the component converts that into a thrown Error so sendMessage's catch block can render it in the chat log. The i18n key step5.requestFailed is only the fallback text when the server gave no error string.

Source

Thrown at frontend/src/components/Step5Interaction.vue:708

      role: msg.role,
      content: msg.content
    }))
  
  const res = await chatWithReport({
    simulation_id: props.simulationId,
    message: message,
    chat_history: historyForApi
  })
  
  if (res.success && res.data) {
    chatHistory.value.push({
      role: 'assistant',
      content: res.data.response || res.data.answer || t('step5.noResponse'),
      timestamp: new Date().toISOString()
    })
    addLog(t('log.reportAgentReplied'))
  } else {
    throw new Error(res.error || t('step5.requestFailed'))
  }
}

const sendToAgent = async (message) => {
  if (!selectedAgent.value || selectedAgentIndex.value === null) {
    throw new Error(t('step5.selectAgentFirst'))
  }
  
  addLog(t('log.sendToAgent', { name: selectedAgent.value.username, message: message.substring(0, 50) }))
  
  // Build prompt with chat history
  let prompt = message
  if (chatHistory.value.length > 1) {
    const historyContext = chatHistory.value
      .slice(0, -1)
      .slice(-6)
      .map(msg => `${msg.role === 'user' ? '提问者' : '你'}:${msg.content}`)
      .join('\n')

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Inspect the res.error value and the browser Network tab response for the chatWithReport call to get the real backend reason.
  2. Verify props.simulationId matches an existing, still-active simulation (check the backend simulation listing endpoint).
  3. Check backend logs for the report-agent chat route for stack traces (LLM provider errors, missing API keys, timeouts).
  4. If it is an intermittent timeout from a slow LLM, increase the request timeout for the chat endpoint in the API client.
  5. If the server returns no error field, improve the backend route to always populate error on failure so users see the true cause instead of the generic i18n fallback.

Example fix

// before
const res = await chatWithReport({ simulation_id: props.simulationId, message, chat_history: historyForApi })
if (res.success && res.data) { /* ... */ } else {
  throw new Error(res.error || t('step5.requestFailed'))
}

// after - surface HTTP status detail and rethrow only once
const res = await chatWithReport({ simulation_id: props.simulationId, message, chat_history: historyForApi })
if (!res.success) {
  throw new Error(res.error || `Chat request failed (HTTP ${res.status ?? 'unknown'})`)
}
if (!res.data) {
  throw new Error(t('step5.noResponse'))
}
chatHistory.value.push({ role: 'assistant', content: res.data.response || res.data.answer || t('step5.noResponse'), timestamp: new Date().toISOString() })
Defensive patterns

Strategy: try-catch

Validate before calling

if (!props.simulationId) {
  addLog(t('log.invalidSimulation'))
  return
}
const historyForApi = chatHistory.value.slice(0, -1).slice(-10).map(m => ({ role: m.role, content: m.content }))
if (historyForApi.some(m => !m.content)) {
  chatHistory.value = chatHistory.value.filter(m => m.content)
}

Type guard

function isChatSuccess(res) {
  return Boolean(res && res.success === true && res.data && (res.data.response || res.data.answer))
}

Try / catch

try {
  const res = await chatWithReport({ simulation_id: props.simulationId, message, chat_history: historyForApi })
  if (!res.success || !res.data) throw new Error(res.error || t('step5.requestFailed'))
  /* push assistant message */
} catch (err) {
  addLog(t('log.sendFailed', { error: err.message }))
  chatHistory.value.push({ role: 'assistant', content: t('step5.errorOccurred', { error: err.message }), timestamp: new Date().toISOString() })
} finally {
  isSending.value = false
}

Prevention

When it happens

Trigger: Calling chatWithReport({ simulation_id, message, chat_history }) when: the simulation_id does not exist or has expired server-side; the report-generation Celery/LLM task failed; the backend returned 4xx/5xx that the request wrapper flattens into { success: false }; or the response succeeded but data was null/undefined.

Common situations: Session/simulation no longer running (page left open past backend TTL), report agent pipeline crashed mid-generation, backend restarted between page load and chat send, or a proxy/gateway (502/504) intercepting the request during LLM long-polls.

Related errors


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/bdf897173fca40ed. Report an issue: GitHub.