{"record":{"id":"5b61a0cd1e419b26","repo":"666ghj/MiroFish","slug":"t-step5-selectagentfirst","errorCode":null,"errorMessage":"t('step5.selectAgentFirst')","messagePattern":"t\\('step5\\.selectAgentFirst'\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"frontend/src/components/Step5Interaction.vue","lineNumber":714,"sourceCode":"    message: message,\n    chat_history: historyForApi\n  })\n  \n  if (res.success && res.data) {\n    chatHistory.value.push({\n      role: 'assistant',\n      content: res.data.response || res.data.answer || t('step5.noResponse'),\n      timestamp: new Date().toISOString()\n    })\n    addLog(t('log.reportAgentReplied'))\n  } else {\n    throw new Error(res.error || t('step5.requestFailed'))\n  }\n}\n\nconst sendToAgent = async (message) => {\n  if (!selectedAgent.value || selectedAgentIndex.value === null) {\n    throw new Error(t('step5.selectAgentFirst'))\n  }\n  \n  addLog(t('log.sendToAgent', { name: selectedAgent.value.username, message: message.substring(0, 50) }))\n  \n  // Build prompt with chat history\n  let prompt = message\n  if (chatHistory.value.length > 1) {\n    const historyContext = chatHistory.value\n      .slice(0, -1)\n      .slice(-6)\n      .map(msg => `${msg.role === 'user' ? '提问者' : '你'}：${msg.content}`)\n      .join('\\n')\n    prompt = `以下是我们之前的对话：\\n${historyContext}\\n\\n现在我的新问题是：${message}`\n  }\n  \n  const res = await interviewAgents({\n    simulation_id: props.simulationId,\n    interviews: [{","sourceCodeStart":696,"sourceCodeEnd":732,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/frontend/src/components/Step5Interaction.vue#L696-L732","documentation":"Thrown at the top of sendToAgent when the user tries to chat with an individual agent but selectedAgent is falsy or selectedAgentIndex is null. It is a client-side precondition check: the chat target dropdown defaults to report_agent, and switching to a single agent requires clicking an agent in the list, which sets both refs. The throw is a guard, not a server condition.","triggerScenarios":"chatTarget is 'agent' (single-agent mode) but the user never clicked an agent profile; the selected agent was removed after profiles reloaded (selection not reset together); or a keyboard/enter submission races the click that sets selectedAgent before the watcher runs.","commonSituations":"UI lets the user type and press Enter before selecting an agent (send button/input not disabled in agent mode); agent list refresh clears selectedAgent but leaves chatTarget on 'agent'; profile array re-indexed after a simulation reload while an old index stayed selected.","solutions":["Select an agent in the agent list before sending, or switch the chat target back to report_agent.","Disable the chat input/send button while chatTarget === 'agent' && !selectedAgent so the state is unreachable.","When profiles are reloaded or the simulation changes, reset chatTarget to 'report_agent' and clear selectedAgent/selectedAgentIndex together.","Validate selectedAgentIndex < profiles.length before sending, since the index is used to build the interview payload."],"exampleFix":"// before\nconst sendToAgent = async (message) => {\n  if (!selectedAgent.value || selectedAgentIndex.value === null) {\n    throw new Error(t('step5.selectAgentFirst'))\n  }\n\n// after - block at the UI boundary so the throw never fires\nconst canChatWithAgent = computed(() =>\n  chatTarget.value !== 'agent' || (selectedAgent.value !== null && selectedAgentIndex.value !== null)\n)\n// template: <input :disabled=\"!canChatWithAgent\" ...>\nconst sendToAgent = async (message) => {\n  if (!selectedAgent.value || selectedAgentIndex.value === null) {\n    throw new Error(t('step5.selectAgentFirst'))\n  }","handlingStrategy":"validation","validationCode":"const canSendToAgent = computed(() =>\n  chatTarget.value !== 'agent' ||\n  (selectedAgent.value !== null && selectedAgentIndex.value !== null && selectedAgentIndex.value < profiles.value.length)\n)\n// template: <textarea :disabled=\"chatTarget === 'agent' && !canSendToAgent\">","typeGuard":"function hasSelectedAgent(sel, idx, profiles) {\n  return sel != null && idx !== null && Number.isInteger(idx) && idx >= 0 && idx < profiles.length\n}","tryCatchPattern":null,"preventionTips":["Disable the chat input whenever chatTarget is 'agent' and no agent is selected.","Reset chatTarget to 'report_agent' and clear selectedAgent/selectedAgentIndex together whenever profiles reload.","Re-validate selectedAgentIndex against profiles.length before building the interview payload.","Show a visible hint in agent mode until an agent is picked."],"tags":["frontend","vue","validation","precondition","chat"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}