{"record":{"id":"7a40ddf270aa0d27","repo":"ZhuLinsen/daily_stock_analysis","slug":"result1-get-error-unknown","errorCode":null,"errorMessage":"result1.get('error', 'unknown')","messagePattern":"result1\\.get\\('error', 'unknown'\\)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/notification_sender/slack_sender.py","lineNumber":204,"sourceCode":"        \"\"\"\n        # Bot 模式：使用新版文件上传 API\n        if self._use_bot:\n            headers = {'Authorization': f'Bearer {self._slack_bot_token}'}\n            try:\n                # Step 1: 获取上传 URL\n                resp1 = requests.post(\n                    'https://slack.com/api/files.getUploadURLExternal',\n                    headers=headers,\n                    data={\n                        'filename': 'report.png',\n                        'length': len(image_bytes),\n                    },\n                    timeout=30,\n                )\n                result1 = resp1.json()\n                if not result1.get(\"ok\"):\n                    logger.error(\"Slack 获取上传 URL 失败: %s\", result1.get('error', 'unknown'))\n                    raise RuntimeError(result1.get('error', 'unknown'))\n\n                upload_url = result1['upload_url']\n                file_id = result1['file_id']\n\n                # Step 2: 上传文件内容（raw body，不能用 multipart）\n                resp2 = requests.post(\n                    upload_url,\n                    data=image_bytes,\n                    headers={'Content-Type': 'application/octet-stream'},\n                    timeout=30,\n                )\n                if resp2.status_code != 200:\n                    logger.error(\"Slack 文件上传失败: HTTP %s\", resp2.status_code)\n                    raise RuntimeError(f\"HTTP {resp2.status_code}\")\n\n                # Step 3: 完成上传并分享到频道\n                resp3 = requests.post(\n                    'https://slack.com/api/files.completeUploadExternal',","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/notification_sender/slack_sender.py#L186-L222","documentation":"RuntimeError raised in the Slack sender's image upload flow when the files.getUploadURLExternal API call returns ok=false. The raised message is the Slack error code from the response (retrieved via result1.get('error','unknown')), such as invalid_auth, channel_not_found, or file_not_found. It aborts the report-image notification for that send.","triggerScenarios":"POSTing to https://slack.com/api/files.getUploadURLExternal with a bad/expired bot token (invalid_auth, token_expired), a bot not invited to the target channel (channel_not_found / not_in_channel), missing files:write scope (missing_scope), or an invalid length parameter. Any ok=false response triggers it.","commonSituations":"Rotated or revoked Slack bot tokens; forgetting to reinstall the Slack app after adding the files:write OAuth scope; the bot was never invited to the configured channel; workspace admins revoked the app; token for a different workspace than the channel.","solutions":["Check the logged Slack error code right before the raise — it names the exact cause","For auth errors, refresh SLACK_BOT_TOKEN and ensure the app has chat:write and files:write scopes, then reinstall the app in the workspace","For channel errors, verify SLACK_CHANNEL_ID and invite the bot to that channel (/invite @bot)","For missing_scope, add the required scopes in the Slack app config and re-authorize"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"def slack_token_looks_valid(token: str) -> bool:\n    return bool(token) and token.startswith(\"xoxb-\") and len(token) > 20","typeGuard":null,"tryCatchPattern":"try:\n    sender.send_image(channel_id, image_bytes)\nexcept RuntimeError as exc:\n    if str(exc) in {\"invalid_auth\", \"token_expired\", \"account_inactive\"}:\n        alert_ops(\"Slack token invalid/expired — rotate SLACK_BOT_TOKEN\")\n    elif str(exc) in {\"channel_not_found\", \"not_in_channel\"}:\n        alert_ops(f\"Slack bot lacks access to channel {channel_id}\")\n    raise","preventionTips":["Verify the bot token and files:write/chat:write scopes at app startup with an auth.test call","Invite the bot to the target channel as part of setup runbooks","Monitor Slack token rotation events and rotate config proactively"],"tags":["slack","notification","auth","api","upload"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}