{"record":{"id":"80bf6798f41a32da","repo":"ZhuLinsen/daily_stock_analysis","slug":"intelligence-source-not-found-source-id","errorCode":null,"errorMessage":"Intelligence source not found: {source_id}","messagePattern":"Intelligence source not found: (.+?)","errorType":"exception","errorClass":"IntelligenceServiceError","httpStatus":404,"severity":"error","filePath":"src/services/intelligence_service.py","lineNumber":255,"sourceCode":"            \"total\": total,\n            \"page\": max(1, int(filters.get(\"page\") or 1)),\n            \"page_size\": max(1, min(int(filters.get(\"page_size\") or 50), 100)),\n        }\n\n    def test_source(self, payload: Dict[str, Any]) -> Dict[str, Any]:\n        fields = self._normalize_source_fields(payload)\n        entries = self._fetch_feed_entries(fields, limit=min(5, self.config.news_intel_max_items_per_source))\n        return {\n            \"ok\": True,\n            \"source\": self._redact_source_fields(fields),\n            \"fetched_count\": len(entries),\n            \"sample_items\": [self._feed_entry_to_dict(entry) for entry in entries[:5]],\n        }\n\n    def fetch_source(self, source_id: int, *, dry_run: bool = False) -> Dict[str, Any]:\n        source = self.repo.get_source(source_id)\n        if source is None:\n            raise IntelligenceServiceError(f\"Intelligence source not found: {source_id}\")\n        if not source.enabled:\n            raise IntelligenceServiceError(f\"Intelligence source is disabled: {source_id}\")\n        now = datetime.now()\n        try:\n            entries = self._fetch_feed_entries(self._source_to_fields(source), limit=self.config.news_intel_max_items_per_source)\n            item_fields = [self._entry_to_item_fields(entry, source, now) for entry in entries]\n            saved = 0 if dry_run else self.repo.upsert_items(item_fields)\n            deleted = 0 if dry_run else self.repo.apply_retention(self.config.news_intel_retention_days)\n            if not dry_run:\n                self.repo.update_source_status(source.id, status=\"success\", error=None, fetched_at=now)\n            return {\n                \"ok\": True,\n                \"source_id\": source.id,\n                \"fetched_count\": len(entries),\n                \"saved_count\": saved,\n                \"retention_deleted\": deleted,\n                \"dry_run\": dry_run,\n                \"sample_items\": [self._feed_entry_to_dict(entry) for entry in entries[:5]],","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/services/intelligence_service.py#L237-L273","documentation":"fetch_source raises IntelligenceServiceError when repo.get_source(source_id) returns None — the id does not exist in the intelligence_sources table (already deleted, or never created).","triggerScenarios":"POST /fetch for a source id deleted in another tab/session; id from a stale list response; hand-crafted id.","commonSituations":"Two admins editing concurrently; frontend state holding a deleted row; re-running an old request after DB reset.","solutions":["Refresh the source list before acting on it; remove deleted rows from UI state on delete confirmation","Handle this error as 404-equivalent and stop retrying — retrying will not resurrect the row","If id seems valid, verify against list_sources output"],"exampleFix":"# before\nsvc.fetch_source(deleted_id)  # -> not found\n# after\nsrc = next((s for s in svc.list_sources()['items'] if s['id']==sid), None)\nif src is None:\n    refresh_ui_list()\nelse:\n    svc.fetch_source(sid)","handlingStrategy":"try-catch","validationCode":"src = svc.repo.get_source(source_id)  # or via API GET detail\nif src is None:\n    remove_from_ui_list(source_id); refresh()","typeGuard":null,"tryCatchPattern":"try:\n    svc.fetch_source(sid)\nexcept IntelligenceServiceError as e:\n    if 'not found' in str(e):\n        drop_stale_row(sid)  # 404-equivalent: stop retrying\n    else:\n        raise","preventionTips":["Refresh the source list after any delete before issuing fetches","Drop rows from UI state on delete confirmation, not lazily","Don't auto-retry not-found errors — they are permanent"],"tags":["intelligence","not-found","stale-state"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}