ZhuLinsen/daily_stock_analysis · error · RuntimeError

HTTP {resp2.status_code}

Error message

HTTP {resp2.status_code}

What it means

RuntimeError raised in the Slack sender when step 2 of the file upload — POSTing the raw PNG bytes to the upload_url returned by files.getUploadURLExternal — responds with a non-200 HTTP status. Unlike step 1, this endpoint is not the JSON API, so only the HTTP status is checked and surfaced as 'HTTP <status>'.

Source

Thrown at src/notification_sender/slack_sender.py:218

                )
                result1 = resp1.json()
                if not result1.get("ok"):
                    logger.error("Slack 获取上传 URL 失败: %s", result1.get('error', 'unknown'))
                    raise RuntimeError(result1.get('error', 'unknown'))

                upload_url = result1['upload_url']
                file_id = result1['file_id']

                # Step 2: 上传文件内容(raw body,不能用 multipart)
                resp2 = requests.post(
                    upload_url,
                    data=image_bytes,
                    headers={'Content-Type': 'application/octet-stream'},
                    timeout=30,
                )
                if resp2.status_code != 200:
                    logger.error("Slack 文件上传失败: HTTP %s", resp2.status_code)
                    raise RuntimeError(f"HTTP {resp2.status_code}")

                # Step 3: 完成上传并分享到频道
                resp3 = requests.post(
                    'https://slack.com/api/files.completeUploadExternal',
                    headers={**headers, 'Content-Type': 'application/json'},
                    json={
                        'files': [{'id': file_id, 'title': '股票分析报告'}],
                        'channel_id': self._slack_channel_id,
                    },
                    timeout=30,
                )
                result3 = resp3.json()
                if result3.get("ok"):
                    logger.info("Slack Bot 图片发送成功")
                    return True
                logger.error("Slack 完成上传失败: %s", result3.get('error', 'unknown'))
            except Exception as e:
                logger.error("Slack Bot 图片发送异常: %s", e)

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Retry the whole 3-step upload once — expired/one-shot upload URLs and transient 5xx are common and a fresh sequence fixes them
  2. Ensure the 'length' value passed to files.getUploadURLExternal equals len(image_bytes) exactly
  3. Check for proxies/interceptors that alter binary bodies or strip headers, and bypass them for slack.com
  4. If persistent, capture resp2 body/text for the specific status code and consult Slack upload docs for that failure
Defensive patterns

Strategy: retry

Validate before calling

def upload_lengths_consistent(length_declared: int, payload: bytes) -> bool:
    return length_declared == len(payload)

Try / catch

for attempt in range(2):
    try:
        send_slack_image(channel, image_bytes)
        break
    except RuntimeError as exc:
        if str(exc).startswith("HTTP ") and attempt == 0:
            time.sleep(1)  # fresh upload URL next round
            continue
        raise

Prevention

When it happens

Trigger: The POST of image_bytes (Content-Type: application/octet-stream) to upload_url returns 4xx/5xx: 403 when the pre-signed upload URL expired or was already used, 400 when the body length does not match the 'length' declared in step 1, 5xx on Slack infra issues, or timeouts/empty bytes producing edge failures.

Common situations: Declaring a length different from the actual image byte count; retrying an already-consumed upload URL; long delays between step 1 and step 2 letting the URL expire; corporate proxies mangling binary request bodies; transient Slack upload-host errors.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/feb9f8f2750b9531. Report an issue: GitHub.