Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for Slack By James Joyner IV · · 8 min read Last reviewed Jul 2026

Slack Error Guide: 'not_authed' — Send a Token With Every Call

Quick answer

Fix the Slack API not_authed error: no authentication token reached the endpoint. Diagnose missing headers, unset env vars, wrong content type, and empty tokens with curl.

  • #slack
  • #api
  • #troubleshooting
  • #errors
Free toolkit

Stuck on this Slack error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Overview

not_authed is Slack’s way of saying no authentication token arrived at all. It is not “your token is wrong” — that is invalid_auth. It is “there was nothing to authenticate”: the request reached the Web API endpoint carrying no bearer token in the Authorization header and no token parameter in the body. Almost every time, the token variable was empty, the header was dropped, or the token landed in the wrong place for the content type you used.

The response body looks like this:

{
    "ok": false,
    "error": "not_authed"
}

It surfaces on any authenticated Web API method, so auth.test reproduces it instantly — and because the fault is a missing credential rather than a bad one, the fix is almost always in how the request is built, not in the token itself.

Symptoms

  • Every authenticated call returns not_authed, regardless of endpoint.
  • The same request works from your laptop but fails in CI or a container.
  • A newly deployed service fails from its very first call — it never had a token.
  • echo "$SLACK_BOT_TOKEN" prints an empty line where the request runs.
  • Switching from Authorization: Bearer to a form field (or vice versa) makes it appear.
curl -s "https://slack.com/api/auth.test" \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN"
{
    "ok": false,
    "error": "not_authed"
}

If $SLACK_BOT_TOKEN is unset, the header becomes literally Authorization: Bearer with an empty value — Slack sees no token and returns not_authed.

Common Root Causes

1. The token environment variable is unset or empty

The single most common cause. The variable is not exported into the process that makes the call — a different shell, a systemd unit without Environment=, a CI job missing the secret, or a container that never received it.

# Prove it before blaming Slack:
printenv SLACK_BOT_TOKEN | head -c 8
echo "len=${#SLACK_BOT_TOKEN}"
len=0

A length of 0 means the header/param you build is empty and every call will not_authed.

2. The Authorization header was never attached

An HTTP client that silently drops or overrides headers, a proxy that strips Authorization, or code that forgot the header entirely.

# No header at all:
curl -s "https://slack.com/api/auth.test"
{
    "ok": false,
    "error": "not_authed"
}

3. Token in the body, but the wrong content type

For methods that accept a token form parameter, you must actually send it as form-encoded data. Posting a JSON body with a token field to a application/x-www-form-urlencoded endpoint (or omitting -d) means Slack never parses the token.

# JSON body where a form field was expected — token not seen:
curl -s -X POST "https://slack.com/api/chat.postMessage" \
  -H "Content-Type: application/json" \
  -d '{"channel":"C0123","text":"hi"}'
{
    "ok": false,
    "error": "not_authed"
}

The chat.postMessage JSON form requires the token in the Authorization header; only the header carries it here.

4. A secret manager returned nothing

The lookup key was wrong, the secret was never created, or the fetch failed and the code fell through to an empty string instead of erroring.

# Simulate an empty secret fetch feeding the call:
TOKEN=""
curl -s "https://slack.com/api/auth.test" -H "Authorization: Bearer $TOKEN"
{
    "ok": false,
    "error": "not_authed"
}

5. Whitespace or quotes ate the token

A trailing newline, surrounding quotes copied from a config file, or a .env line like SLACK_BOT_TOKEN="xoxb-..." parsed literally can leave the value empty or malformed enough that no token is sent.

6. Calling a method that needs auth without intending to

A few endpoints (like api.test) need no token, but most do. Pointing an unauthenticated client at conversations.list or chat.postMessage yields not_authed until you add credentials.

Diagnostic Workflow

Step 1: Confirm connectivity WITHOUT auth

api.test needs no token. If it succeeds, your network and endpoint are fine and the problem is purely the missing credential.

curl -s "https://slack.com/api/api.test"
{
    "ok": true
}

Step 2: Prove the token variable is actually populated

echo "len=${#SLACK_BOT_TOKEN}"
printenv SLACK_BOT_TOKEN | head -c 5
len=0

A zero length is your answer — fix the environment, not the API call.

Step 3: Call auth.test with the header explicitly

curl -sv "https://slack.com/api/auth.test" \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" 2>&1 | grep -i '> authorization'
> authorization: Bearer

An empty value after Bearer confirms nothing is being sent.

Step 4: Verify inside the actual runtime, not your laptop

Environment mismatches are the classic gotcha. Run the same check in the exact process:

# In the container/CI step that fails:
sh -c 'echo "len=${#SLACK_BOT_TOKEN}"; curl -s https://slack.com/api/auth.test -H "Authorization: Bearer $SLACK_BOT_TOKEN"'
len=0
{"ok":false,"error":"not_authed"}

Step 5: Confirm the fix

Once the token is present, auth.test returns identity:

curl -s "https://slack.com/api/auth.test" \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN"
{
    "ok": true,
    "team": "ACME Prod",
    "user_id": "U0BOTBOT01"
}

Example Root Cause Analysis

A deploy-notification bot works perfectly in local testing but returns not_authed on its first call from the CI pipeline. Locally the developer has SLACK_BOT_TOKEN in their shell profile; CI does not.

# Reproduced inside the failing CI job:
sh -c 'echo "len=${#SLACK_BOT_TOKEN}"'
len=0

The pipeline references the secret as SLACK_TOKEN, but the code reads SLACK_BOT_TOKEN. The variable the code reads is never set, so it builds Authorization: Bearer with an empty value — hence not_authed rather than invalid_auth.

Fix: align the names and fail fast if the token is empty instead of sending a blank header.

# Guard at startup:
: "${SLACK_BOT_TOKEN:?SLACK_BOT_TOKEN is not set}"
curl -s "https://slack.com/api/auth.test" -H "Authorization: Bearer $SLACK_BOT_TOKEN"
{"ok": true, "team": "ACME Prod", "user_id": "U0BOTBOT01"}

The mismatched variable name — not the token — was the whole problem.

Prevention Best Practices

  • Fail fast at startup if the token variable is empty (: "${SLACK_BOT_TOKEN:?}") so a missing secret is a boot error, not a runtime not_authed.
  • Standardize on one variable name across code, CI, and systemd units; most not_authed incidents are a name mismatch.
  • Prefer the Authorization: Bearer header over form token params — it is consistent across JSON and form endpoints.
  • Run auth.test as a health check so a missing credential surfaces at deploy, not during an incident.
  • Strip quotes/whitespace when loading .env files; a value that parses to empty sends no token.
  • Distinguish in code: not_authed means “no token sent — fix the request”, while invalid_auth means “token rejected — replace it”.

Quick Command Reference

# Endpoint reachable without auth?
curl -s "https://slack.com/api/api.test"

# Is the token variable actually populated?
echo "len=${#SLACK_BOT_TOKEN}"

# Authenticated identity check
curl -s "https://slack.com/api/auth.test" -H "Authorization: Bearer $SLACK_BOT_TOKEN"

# See exactly what header is sent
curl -sv "https://slack.com/api/auth.test" \
  -H "Authorization: Bearer $SLACK_BOT_TOKEN" 2>&1 | grep -i '> authorization'

# Fail fast if unset
: "${SLACK_BOT_TOKEN:?SLACK_BOT_TOKEN is not set}"

Conclusion

not_authed means the request carried no credential at all — distinct from invalid_auth, where a token was sent and rejected. The usual root causes:

  1. The token environment variable is unset or empty in the runtime.
  2. The Authorization header was never attached (or a proxy stripped it).
  3. The token was placed in a body/param the endpoint’s content type never parsed.
  4. A secret-manager lookup returned an empty string.
  5. Whitespace or quotes reduced the token to nothing.
  6. An unauthenticated client hit a method that requires auth.

Confirm the endpoint works without auth via api.test, prove the token variable is populated in the failing runtime, then attach it as a bearer header and verify with auth.test. For fast triage of whether nothing was sent versus a bad token, the incident assistant can tell them apart. See more in Slack guides.

Free download · 368-page PDF

Fixed it? Get 500 Slack & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.