chenhg5/cc-connect · critical
webex: unauthorized (401) — check bot token
Error message
webex: unauthorized (401) — check bot token
What it means
errUnauthorized is a sentinel error declared in platform/webex/client.go. The Webex REST client wraps it with the failing operation name when webexapis.com returns HTTP 401, so callers can detect auth failure via errors.Is and stop retrying instead of hammering the API. It always indicates the bot token was rejected.
Source
Thrown at platform/webex/client.go:24
"encoding/json"
"errors"
"fmt"
"io"
"mime"
"mime/multipart"
"net/http"
"strconv"
"strings"
"time"
)
const webexBaseURL = "https://webexapis.com/v1"
// maxRetryAfter caps how long we honor a 429 Retry-After header before retrying.
const maxRetryAfter = 60 * time.Second
// errUnauthorized signals a 401 from the Webex API so callers can stop retrying.
var errUnauthorized = errors.New("webex: unauthorized (401) — check bot token")
// webexClient abstracts the Webex REST API so tests can stub it.
type webexClient interface {
GetMe(ctx context.Context) (*person, error)
CreateDevice(ctx context.Context) (*device, error)
DeleteDevice(ctx context.Context, deviceURL string) error
GetMessage(ctx context.Context, id string) (*message, error)
DownloadFile(ctx context.Context, url string) (*downloadedFile, error)
PostMessage(ctx context.Context, roomID, parentID, markdown string) error
PostFile(ctx context.Context, roomID string, f *downloadedFile) error
}
// httpClient is the real webexClient backed by net/http.
type httpClient struct {
token string
hc *http.Client
baseURL string // Webex REST base; overridable in tests.
}View on GitHub (pinned to 4000b2338a)
Solutions
- Regenerate the Webex bot access token in the Webex Developer portal and update it in config.toml, then restart cc-connect.
- Verify the token with curl: `curl -H 'Authorization: Bearer <token>' https://webexapis.com/v1/people/me` — expect 200.
- Check for trailing whitespace/newlines or env-var interpolation mistakes in the configured token.
- Ensure you are using a bot token (not an OAuth user token) with the required scopes.
Example fix
// before (config.toml) [platforms.webex] access_token = "OLD_EXPIRED_TOKEN" // after [platforms.webex] access_token = "Y2x...newly-generated-bot-token"
Defensive patterns
Strategy: try-catch
Validate before calling
// before starting cc-connect, verify the bot token:
tok := cfg.WebexAccessToken
req, _ := http.NewRequest("GET", "https://webexapis.com/v1/people/me", nil)
req.Header.Set("Authorization", "Bearer "+tok)
resp, err := http.DefaultClient.Do(req)
if err != nil || resp.StatusCode != 200 {
return fmt.Errorf("webex bot token invalid (status %v)", resp.StatusCode)
} Try / catch
if errors.Is(err, webex.ErrUnauthorized) {
// non-retryable: alert and stop, don't loop
slog.Error("webex auth rejected; token must be refreshed", "err", err)
return
} Prevention
- Validate the token with GET /people/me at startup and on 401
- Track token expiry; Webex bot tokens can be revoked/rotated — redeploy config after rotation
- Trim whitespace/newlines when copying tokens into config
- Use a bot token, not a user OAuth token, and store it via env/secret manager
When it happens
Trigger: Any API call routed through the client's do/request helpers (GetMe, CreateDevice, message fetches) that receives HTTP 401; also propagated by TestGetMessageUnauthorized and connectLoop when the initial connection or subsequent requests are rejected with 401.
Common situations: Expired or revoked Webex bot access token in config.toml; token copied with whitespace or from the wrong bot; environment where the token was rotated after startup; wrong integration using a user token instead of a bot token.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/7f9fbd768ebb84fd.
Report an issue: GitHub.