{"record":{"id":"9a19ba2d9e4b2f25","repo":"ZhuLinsen/daily_stock_analysis","slug":"desktop-backend-url-is-unavailable","errorCode":null,"errorMessage":"Desktop backend URL is unavailable","messagePattern":"Desktop backend URL is unavailable","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/dsa-desktop/main.js","lineNumber":1354,"sourceCode":"}\n\nfunction buildMainPageUrl(port, timestamp = Date.now(), host = DESKTOP_BACKEND_DEFAULT_HOST) {\n  const url = new URL(buildBackendUrl(host, port, '/'));\n  url.searchParams.set('desktop_version', resolveDesktopVersion() || 'unknown');\n  url.searchParams.set('cache_bust', String(timestamp));\n  return url.toString();\n}\n\nfunction buildDesktopShareImageUrl(pageUrl, recordId, expectedBackendOrigin = '') {\n  if (!Number.isSafeInteger(recordId) || recordId <= 0) {\n    throw new Error('Invalid share image record ID');\n  }\n\n  let page;\n  try {\n    page = new URL(pageUrl);\n  } catch (_error) {\n    throw new Error('Desktop backend URL is unavailable');\n  }\n\n  let expectedOrigin = page.origin;\n  if (expectedBackendOrigin) {\n    try {\n      expectedOrigin = new URL(expectedBackendOrigin).origin;\n    } catch (_error) {\n      throw new Error('Desktop backend origin is invalid');\n    }\n  }\n  if (page.protocol !== 'http:' || !page.port || page.origin !== expectedOrigin) {\n    throw new Error('Desktop share images require the configured backend origin');\n  }\n\n  return new URL(\n    `/api/v1/history/${recordId}/share-image-html`,\n    page.origin\n  ).toString();","sourceCodeStart":1336,"sourceCodeEnd":1372,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/apps/dsa-desktop/main.js#L1336-L1372","documentation":"Raised by PortfolioService.query_trades when a non-empty symbol filter produces no usable filter values. _build_symbol_filter_values (portfolio_service.py:1299) normalizes the symbol via canonical_stock_code/normalize_stock_code; if normalization yields an empty string, the filter list is empty and the service refuses to run an unfiltered query that the user intended to be filtered.","triggerScenarios":"GET /portfolio/trades?symbol=%20%20%20%20 or ?symbol=@@@, or query_trades(symbol=\"NOSUCH!!!\") where canonicalization returns ''. Note the guard is only reached when symbol.strip() is truthy, so purely empty strings skip filtering instead of raising.","commonSituations":"Passing punctuation-only or placeholder strings ('-', 'n/a', '--') from a CSV import or UI dropdown; symbols containing only whitespace that survived an outer strip but fail canonicalization; test fixtures with invented tickers the provider cannot canonicalize.","solutions":["Send a real ticker in a supported format ('600519', 'HK00700', 'AAPL')","If the intent is 'no filter', omit the symbol parameter entirely or pass an empty string","Validate symbols against the same canonicalizer (data_provider.base.canonical_stock_code) before calling query_trades","Sanitize upstream data feeds that inject placeholder values into symbol fields"],"exampleFix":"# before\nsvc.query_trades(symbol=\"-\")\n# after\nsvc.query_trades(symbol=None)  # no symbol filter","handlingStrategy":"validation","validationCode":"from data_provider.base import canonical_stock_code, normalize_stock_code\n\ndef symbol_filter_ok(symbol):\n    if symbol is None or not symbol.strip():\n        return True  # no filter intended\n    return bool(canonical_stock_code(normalize_stock_code(symbol.strip())))","typeGuard":"def is_queryable_symbol(symbol: str | None) -> bool:\n    \"\"\"False when query_trades would raise 'symbol is invalid'.\"\"\"\n    if not (symbol or \"\").strip():\n        return True\n    return bool(canonical_stock_code(symbol))","tryCatchPattern":"try:\n    svc.query_trades(symbol=s)\nexcept ValueError as exc:\n    if \"symbol is invalid\" in str(exc):\n        return {\"items\": [], \"total\": 0}  # treat as no matches\n    raise","preventionTips":["Omit the symbol param entirely when no filter is wanted","Use real tickers, never placeholders like '-' or 'n/a'","Pre-validate filter input with the provider's canonicalizer"],"tags":["portfolio","validation","symbol","query"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}