chenhg5/cc-connect · error
unauthorized
Error message
unauthorized
What it means
The bridge WebSocket endpoint rejects HTTP requests whose authentication check fails. `handleWS` calls `bs.authenticate(r)` before upgrading the connection; on failure it responds 401 with the plain body "unauthorized" and never upgrades. This protects the local bridge from unauthenticated clients connecting to the agent.
Source
Thrown at core/bridge.go:737
// No CORS configured - require same-host (origin must match host)
host := r.Host
if host == "" {
host = r.URL.Host
}
// Parse origin to get host
if idx := strings.Index(origin, "://"); idx > 0 {
originHost := origin[idx+3:]
if originHost == host {
return true
}
}
slog.Warn("bridge: websocket origin mismatch", "origin", origin, "host", host)
return false
}
func (bs *BridgeServer) handleWS(w http.ResponseWriter, r *http.Request) {
if !bs.authenticate(r) {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
// Use a custom upgrader with origin checking
upgrader := websocket.Upgrader{
CheckOrigin: bs.checkOrigin,
}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
slog.Error("bridge: websocket upgrade failed", "error", err)
return
}
slog.Info("bridge: new connection", "remote", conn.RemoteAddr())
bs.handleConnection(conn)
}
View on GitHub (pinned to 4000b2338a)
Solutions
- Set the exact bridge token/secret configured on the BridgeServer in your client's Authorization header (or token query parameter, per authenticate()'s expected location).
- Re-read the token from config.toml after any rotation and restart the client.
- If behind a proxy, ensure the Authorization header is forwarded (e.g. proxy_set_header Authorization $http_authorization).
- Enable debug logs on the bridge to see which auth path failed (missing vs mismatched credential).
Example fix
// before
ws := websocket.Dialer{}
conn, _, err := ws.Dial("ws://127.0.0.1:8848/ws", nil)
// after
hdr := http.Header{"Authorization": []string{"Bearer " + cfg.BridgeToken}}
conn, resp, err := ws.Dial("ws://127.0.0.1:8848/ws", hdr)
if resp != nil && resp.StatusCode == http.StatusUnauthorized {
log.Fatal("bridge token mismatch — update BridgeToken")
} Defensive patterns
Strategy: validation
Validate before calling
func wsURL(u string, token string) error {
if u == "" || token == "" { return errors.New("bridge url and token required") }
parsed, err := url.Parse(u)
if err != nil || (parsed.Scheme != "ws" && parsed.Scheme != "wss") { return errors.New("invalid ws url") }
return nil
} Type guard
func isUnauthorized(resp *http.Response) bool { return resp != nil && resp.StatusCode == http.StatusUnauthorized } Try / catch
conn, resp, err := dialer.Dial(url, authHeader)
if err != nil {
if resp != nil && resp.StatusCode == 401 {
return fmt.Errorf("bridge auth failed: reload BridgeToken")
}
return err
} Prevention
- Load the bridge token from one shared config and reuse it in every client
- Re-distribute tokens after any rotation via the same config file
- Write a startup self-test that connects to the bridge WS and fails fast on 401
- Verify proxies forward the Authorization header
When it happens
Trigger: Opening a WebSocket to the bridge server without the required auth credential, with a wrong or malformed token (e.g. missing Authorization header or query token), or with a token that does not match the configured bridge secret.
Common situations: Client configured with an outdated token after the bridge secret rotated; client omitting the token entirely; reverse proxy stripping the Authorization header; connecting a second tool to the bridge with a guessed/default token.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- webex: unauthorized (401) — check bot token
- cloud_web: register rejected: %s
- cloud_web: register rejected
- subscribe failed: errcode=%d errmsg=%s
- unauthorized
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/3090a7254ff4fa46.
Report an issue: GitHub.