{"record":{"id":"10cfb65f000ccad2","repo":"ZhuLinsen/daily_stock_analysis","slug":"error-10cfb6","errorCode":null,"errorMessage":"当前平台不支持自动安装更新。","messagePattern":"当前平台不支持自动安装更新。","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/dsa-desktop/main.js","lineNumber":1777,"sourceCode":"    setDesktopUpdateState({\n      status: UPDATE_STATUS.ERROR,\n      updateMode: UPDATE_MODE.AUTO,\n      currentVersion: resolveDesktopVersion(),\n      latestVersion: desktopUpdateState?.latestVersion || '',\n      releaseUrl: desktopUpdateState?.releaseUrl || RELEASES_PAGE_URL,\n      checkedAt: new Date().toISOString(),\n      message: `自动更新失败：${message}`,\n    });\n  });\n\n  electronAutoUpdaterConfigured = true;\n  return updater;\n}\n\nasync function performElectronUpdaterCheck({ manual = false } = {}) {\n  const updater = configureElectronAutoUpdater();\n  if (!updater) {\n    throw new Error('当前平台不支持自动安装更新。');\n  }\n  if (electronUpdateCheckInFlight) {\n    return desktopUpdateState;\n  }\n\n  electronUpdateCheckInFlight = true;\n  setDesktopUpdateState({\n    status: UPDATE_STATUS.CHECKING,\n    updateMode: UPDATE_MODE.AUTO,\n    currentVersion: resolveDesktopVersion(),\n    message: manual ? '正在检查桌面端更新...' : '正在后台检查桌面端更新...',\n  });\n\n  try {\n    await updater.checkForUpdates();\n    return desktopUpdateState;\n  } catch (error) {\n    const message = error instanceof Error ? error.message : String(error);","sourceCodeStart":1759,"sourceCodeEnd":1795,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/apps/dsa-desktop/main.js#L1759-L1795","documentation":"Raised when recording a sell whose quantity exceeds the position available as of the trade date. _validate_sell_quantity (portfolio_service.py:691) normalizes the (symbol, market, currency) key, replays events up to trade_date via _calculate_available_quantity, and throws PortfolioOversellError (code=portfolio_oversell) when available + EPS < requested. The exception carries symbol, trade_date, requested_quantity and available_quantity attributes.","triggerScenarios":"add_trade(side='sell', quantity=200) when replay of buys/splits up to that date yields only 100 shares; selling before the covering buy (trade_date earlier than the buy's date); selling in the wrong market/currency key so the matching position is not found; a prior split_adjustment event missing so available quantity is understated.","commonSituations":"Importing broker history out of order (sells before their buys); forgetting to record a stock split before later sells; selling HK shares recorded under a different currency key; rounding: fractional-share lots where cumulative float error exceeds EPS; margin/short selling, which this service does not model.","solutions":["Import trades in chronological order and insert missing buy or split_adjustment events first","Inspect the exception fields (available_quantity, trade_date) to see what the replay thinks you hold","Verify market/currency of the sell match the recorded buys so the (symbol, market, currency) key aligns","If you genuinely short-sell, this service rejects it by design: record only covered sells"],"exampleFix":"# before\nsvc.add_trade(account_id=1, symbol=\"AAPL\", side=\"sell\", quantity=200, trade_date=date(2025,1,10), ...)\n# after\n# record the missing split first, then the sell\nsvc.add_corporate_action(account_id=1, symbol=\"AAPL\", action_type=\"split_adjustment\", split_ratio=2.0, effective_date=date(2025,1,5))\nsvc.add_trade(account_id=1, symbol=\"AAPL\", side=\"sell\", quantity=200, trade_date=date(2025,1,10), ...)","handlingStrategy":"try-catch","validationCode":"def available_before_sell(svc, account_id, symbol, market, currency, trade_date):\n    # replay the same computation the service does\n    return svc._calculate_available_quantity(\n        account_id=account_id,\n        key=(svc._normalize_symbol_for_position(symbol), svc._normalize_market(market), svc._normalize_currency(currency)),\n        as_of_date=trade_date,\n    )","typeGuard":"from src.services.portfolio_service import PortfolioOversellError\n\ndef is_oversell(exc: Exception) -> bool:\n    return isinstance(exc, PortfolioOversellError)","tryCatchPattern":"from src.services.portfolio_service import PortfolioOversellError\n\ntry:\n    svc.add_trade(account_id=a, side=\"sell\", quantity=q, ...)\nexcept PortfolioOversellError as exc:\n    # exc.available_quantity, exc.trade_date, exc.symbol carry the diagnosis\n    raise UserInputError(f\"only {exc.available_quantity} shares available on {exc.trade_date}\") from exc","preventionTips":["Import buys and splits before the sells they cover, in date order","Record split_adjustment events before later sells","Match market and currency of sells to the recorded buys","Read the exception's available_quantity instead of recomputing the position yourself"],"tags":["portfolio","oversell","position","trade","replay"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}