Significant-Gravitas/AutoGPT · error · Error

The ChatGPT verification link is invalid. Close this window

Error message

The ChatGPT verification link is invalid. Close this window and try again.

What it means

This is an HTTP 401 Unauthorized raised by APIKeyAuthenticator, a FastAPI security dependency that validates an API-key header on incoming requests. The header was present (a missing header produces the distinct 'No API key in request' error at line 86-88), but the validator returned a falsy result: either the custom validator rejected the key, or the default_validator's secrets.compare_digest mismatch against expected_token. In this repo the concrete production use is the Postmark webhook router (backend/api/features/postmark/postmark.py:31), which compares the X-Postmark-Webhook-Token header to settings.secrets.postmark_webhook_token.

Source

Thrown at autogpt_platform/backend/backend/api/features/integrations/codex_device_page.py:79

        const statusElement = document.getElementById("status");
        const errorElement = document.getElementById("error");
        const copyButton = document.getElementById("copy-code");

        function fail(message) {
          statusElement.textContent = "";
          errorElement.textContent = message;
        }

        window.addEventListener("pagehide", function () {
          if (!finished) {
            navigator.sendBeacon(window.location.pathname + "/cancel");
          }
        });

        let verificationUrl;
        try {
          verificationUrl = new URL(rawVerificationUrl);
          if (verificationUrl.protocol !== "https:") throw new Error();
        } catch (_) {
          fail("The ChatGPT verification link is invalid. Close this window and try again.");
          return;
        }
        if (!state || !userCode || !loginID) {
          fail("This sign-in attempt is incomplete. Close this window and try again.");
          return;
        }

        codeElement.textContent = userCode;
        linkElement.href = verificationUrl.toString();
        copyButton.addEventListener("click", async function () {
          try {
            await navigator.clipboard.writeText(userCode);
            copyButton.textContent = "Copied";
          } catch (_) {
            fail("Could not copy automatically. Select the code above instead.");
          }

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Compare the exact token value: check the X-Postmark-Webhook-Token header you are sending against settings.secrets.postmark_webhook_token (usually the POSTMARK_WEBHOOK_TOKEN env var in backend/.env or docker-compose env) — they must match character-for-character with no whitespace or quotes.
  2. If the backend log shows the warning 'API key check failed: ...' from api_key_auth.py:119, the expected token is None/empty or non-ASCII: set a valid non-empty ASCII token in the backend environment (backend/.env → secrets section) and restart the service.
  3. If the token was rotated in the Postmark dashboard, copy the new webhook token into the same env var on every deployment that receives the webhook, then restart.
  4. Verify you are hitting the right environment: staging and production often run different secrets; confirm the URL configured in Postmark points at the deployment whose env you inspected.
  5. For local reproduction, curl -X POST http://localhost:8000/api/postmark/ -H 'X-Postmark-Webhook-Token: <token>' -H 'Content-Type: application/json' -d '{...}' and confirm a non-401 response before re-configuring Postmark.

Example fix

# before: backend/.env has a placeholder or nothing
POSTMARK_WEBHOOK_TOKEN=

# after: set the exact token shown in Postmark > Signings > Webhooks (no quotes/spaces)
POSTMARK_WEBHOOK_TOKEN=a1b2c3d4e5f6g7h8

# then restart the backend so Settings() picks it up
# docker compose restart backend   (or: poetry run app)
Defensive patterns

Strategy: validation

Validate before calling

# Before sending/ configuring, verify the token is usable (server side, Python)
import os

POSTMARK_TOKEN = os.environ.get("POSTMARK_WEBHOOK_TOKEN", "")

assert POSTMARK_TOKEN and POSTMARK_TOKEN.isascii() and POSTMARK_TOKEN == POSTMARK_TOKEN.strip(), (
    "POSTMARK_WEBHOOK_TOKEN must be a non-empty, ASCII, whitespace-free value"
)

# Client side: assert the header you are about to send is non-empty and ASCII
# before making the request
# assert token and token.isascii(), 'webhook token missing or non-ASCII'

Try / catch

# Python client: distinguish 'invalid key' (fix the token) from 'missing key' (fix the header)
import requests

resp = requests.post(url, headers={"X-Postmark-Webhook-Token": token}, json=payload, timeout=10)
if resp.status_code == 401:
    detail = resp.json().get("detail")
    if detail == "No API key in request":
        raise RuntimeError("Header X-Postmark-Webhook-Token is not being sent") from None
    if detail == "Invalid API key":
        raise RuntimeError("Token mismatch: rotate/re-copy POSTMARK_WEBHOOK_TOKEN") from None
resp.raise_for_status()

Prevention

When it happens

Trigger: Calling POST /api/postmark/ (or any route secured with Security(api_key_auth)) with an X-Postmark-Webhook-Token header whose value does not exactly match the configured token. Also triggered when the server-side token setting is None or empty and compare_digest raises TypeError (line 117-120 logs a warning and returns False), or when the header value contains non-ASCII characters, which likewise makes compare_digest raise TypeError and get converted to a validation failure.

Common situations: The POSTMARK_WEBHOOK_TOKEN secret is not set (or is set to a placeholder like 'changeme') in backend/.env on the server while a correct token is configured in Postmark's webhook settings; the token was rotated in Postmark but not in the deployment env; trailing whitespace, quotes, or encoding issues from copy-pasting the token into .env or the Postmark dashboard; sending the token in the wrong header (e.g. Authorization or X-API-Key instead of X-Postmark-Webhook-Token gets the 'No API key' 401 instead); multiple deployments (staging vs prod) with different tokens.

Related errors


AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14). Data as JSON: /api/errors/6ee1112a5c872949. Report an issue: GitHub.