zed-industries/zed · error · RuntimeError

Slack webhook returned {response.status_code}: {response.tex

Error message

Slack webhook returned {response.status_code}: {response.text[:200]}

What it means

The incoming-webhook POST returned a status other than 200 and the script raises with the code plus the first 200 characters of the body, which identify the cause: 400 invalid_payload, 404/410 deleted webhook or uninstalled app, 403 wrong workspace, 429 rate limited, 5xx Slack-side incident. There is no retry, so a single blip or an over-long message kills the notification.

Source

Thrown at script/github-guild-board.py:411

    # without every caller having to remember to do it.
    return f"<{url}|{escape_slack(text)}>"


def send_slack(text):
    webhook = os.environ.get("SLACK_WEBHOOK_GUILD_INTERNAL")
    if not webhook:
        raise RuntimeError("SLACK_WEBHOOK_GUILD_INTERNAL is not set")
    message = f"{random.choice(ZEDGAR_QUIPS)} {text}"
    response = requests.post(
        webhook,
        json={
            "text": message,
            "blocks": [{"type": "section", "text": {"type": "mrkdwn", "text": message}}],
        },
        timeout=30,
    )
    if response.status_code != 200:
        raise RuntimeError(
            f"Slack webhook returned {response.status_code}: {response.text[:200]}"
        )


def parse_dt(value):
    return datetime.fromisoformat(value.replace("Z", "+00:00"))


def item_status(item):
    for field_value in (item.get("fieldValues") or {}).get("nodes", []):
        if field_value.get("field", {}).get("name") == STATUS_FIELD:
            return field_value.get("name")
    return None


def latest_check_in_time(comments):
    times = [
        parse_dt(comment["created_at"])

View on GitHub (pinned to bc538def45)

Solutions

  1. Read the body snippet in the message: 'invalid_payload' means trim or split the blocks; 'no_team_found'/404 means recreate the webhook
  2. Truncate each Slack text block to the 3000-char limit before sending
  3. On 429 or 5xx, honor Retry-After and retry once or twice instead of failing the run
  4. If the webhook is dead, create a new one and update the CI secret

Example fix

// before
if response.status_code != 200:
    raise RuntimeError(f"Slack webhook returned {response.status_code}: {response.text[:200]}")

// after
for attempt in range(3):
    if response.status_code == 200:
        break
    if response.status_code in (429, 500, 502, 503) and attempt < 2:
        time.sleep(int(response.headers.get("Retry-After", "2")))
        response = requests.post(webhook, json=payload, timeout=30)
        continue
    raise RuntimeError(f"Slack webhook returned {response.status_code}: {response.text[:200]}")
Defensive patterns

Strategy: retry

Validate before calling

def slack_blocks_within_limits(blocks: list) -> bool:
    return all(
        len(b.get("text", {}).get("text", "")) <= 3000
        for b in blocks
        if b.get("type") == "section"
    )

Type guard

def slack_post_succeeded(response) -> bool:
    return response.status_code == 200

Try / catch

try:
    send_slack(summary)
except RuntimeError as exc:
    status = exc.split(' ')[3] if ' ' in exc else ''
    if ' 429' in str(exc) or ' 5' in str(exc):
        time.sleep(5)
        send_slack(summary)
    else:
        raise

Prevention

When it happens

Trigger: POST to the hooks.slack.com URL with a mrkdwn block exceeding Slack's 3000-char text limit returns 400 invalid_payload; the webhook was deleted or its app uninstalled returns 404/410; bursts faster than roughly one message per second return 429; Slack incidents return 5xx.

Common situations: Guild board summaries growing past block limits as the team grows; channel/webhook removed during a workspace reorg; CI retries spamming the webhook; incoming-webhook app uninstalled for a newer integration.

Related errors


AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16). Data as JSON: /api/errors/4c2d938bc179c56c. Report an issue: GitHub.