{"record":{"id":"9ddfb960aa14dcfe","repo":"ZhuLinsen/daily_stock_analysis","slug":"error-9ddfb9","errorCode":null,"errorMessage":"发送失败","messagePattern":"发送失败","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/dsa-web/src/api/agent.ts","lineNumber":111,"sourceCode":"    const response = await apiClient.get<{ sessions: ChatSessionItem[] }>('/api/v1/agent/chat/sessions', { params: { limit } });\n    return response.data.sessions;\n  },\n  async getChatSessionMessages(sessionId: string): Promise<ChatSessionDetail> {\n    const response = await apiClient.get<ChatSessionDetail>(`/api/v1/agent/chat/sessions/${sessionId}`);\n    return response.data;\n  },\n  async deleteChatSession(sessionId: string): Promise<void> {\n    await apiClient.delete(`/api/v1/agent/chat/sessions/${sessionId}`);\n  },\n  async sendChat(content: string): Promise<{ success: boolean }> {\n    const response = await apiClient.post<{\n      success: boolean;\n      error?: string;\n      message?: string;\n    }>('/api/v1/agent/chat/send', { content });\n    const data = response.data;\n    if (data.success === false) {\n      throw new Error(data.message || '发送失败');\n    }\n    return { success: true };\n  },\n  async chatStream(\n    payload: ChatStreamRequest,\n    options?: ChatStreamOptions,\n  ): Promise<Response> {\n    const base = API_BASE_URL || '';\n    const url = `${base}/api/v1/agent/chat/stream`;\n    try {\n      const response = await fetch(url, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify(payload),\n        credentials: 'include',\n        signal: options?.signal,\n      });\n","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/apps/dsa-web/src/api/agent.ts#L93-L129","documentation":"Raised during quantity replay (_calculate_available_quantity, portfolio_service.py:749) when a split_adjustment event has split_ratio <= 0 (or null, since null coerces to 0.0). The replay cannot scale holdings by a non-positive factor, so the whole sell-validation/snapshot replay aborts with code=validation_error.","triggerScenarios":"A corporate_actions row with split_ratio = 0, negative, or NULL and action_type='split_adjustment' reaching the replay; rows written by an external tool or manual DB edit that bypassed add_corporate_action's validation; NaN serialized as 0.","commonSituations":"Manual SQLite/DB fixes that inserted placeholder 0 in split_ratio; import scripts that map a missing split column to 0 instead of skipping the row; an older code path that allowed null split_ratio before validation was added.","solutions":["Find and fix the offending row: query corporate actions for the symbol where split_ratio IS NULL or <= 0","Delete the corrupt split_adjustment event if it was created by mistake","Re-insert valid splits via add_corporate_action, which enforces split_ratio > 0 at write time","Audit import tooling to skip (not zero-fill) missing split ratios"],"exampleFix":"# before\nsvc.add_corporate_action(account_id=1, symbol=\"AAPL\", action_type=\"split_adjustment\", split_ratio=0, ...)\n# after\nsvc.add_corporate_action(account_id=1, symbol=\"AAPL\", action_type=\"split_adjustment\", split_ratio=2.0, ...)","handlingStrategy":"validation","validationCode":"def split_ratio_ok(ratio):\n    return ratio is not None and ratio > 0","typeGuard":"from numbers import Real\n\ndef is_valid_split_ratio(value: Real | None) -> bool:\n    return value is not None and float(value) > 0.0","tryCatchPattern":"try:\n    svc.get_snapshot(account_id=a)\nexcept ValueError as exc:\n    if \"Invalid split_ratio\" in str(exc):\n        # quarantine the bad corporate action row and retry\n        fix_bad_split_rows(a); svc.get_snapshot(account_id=a)\n    else:\n        raise","preventionTips":["Only write corporate actions via add_corporate_action (validates ratio > 0)","Skip, don't zero-fill, missing split ratios in imports","Audit corporate_actions for split_ratio <= 0 after bulk loads"],"tags":["portfolio","validation","split","replay","data-corruption"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}