glanceapp/glance · error
authentication response returned empty session ID, status co
Error message
authentication response returned empty session ID, status code %d, message '%s'
What it means
Returned by fetchPiholeSessionID when the server answered HTTP 200 yet the parsed session.sid is empty. The API contract was violated: success status without a usable session token, and the message includes the status code and server session.message for diagnosis.
Source
Thrown at internal/glance/widget-dns-stats.go:657
Session struct {
SID string `json:"sid"`
Message string `json:"message"`
} `json:"session"`
}
if err := json.Unmarshal(body, &jsonResponse); err != nil {
return "", fmt.Errorf("parsing authentication response: %v", err)
}
if response.StatusCode != http.StatusOK {
return "", fmt.Errorf(
"authentication request returned status %s with message '%s'",
response.Status, jsonResponse.Session.Message,
)
}
if jsonResponse.Session.SID == "" {
return "", fmt.Errorf(
"authentication response returned empty session ID, status code %d, message '%s'",
response.StatusCode, jsonResponse.Session.Message,
)
}
return jsonResponse.Session.SID, nil
}
func checkPiholeSessionIDIsValid(instanceURL string, client *http.Client, sessionID string) (bool, error) {
request, err := http.NewRequest("GET", instanceURL+"/api/auth", nil)
if err != nil {
return false, fmt.Errorf("creating session ID check request: %v", err)
}
request.Header.Set("x-ftl-sid", sessionID)
response, err := client.Do(request)
if err != nil {
return false, errView on GitHub (pinned to 91324e8de7)
Solutions
- curl -i -X POST {url}/api/auth and verify the body contains {"session":{"sid":"..."}}.
- Bypass the proxy temporarily (hit Pi-hole directly) to isolate where the 200-without-SID originates.
- Update/align the Pi-hole version to a stable v6 release if an edge build changed the payload.
- Check proxy routing rules so /api/auth reaches the Pi-hole FTL webserver.
Defensive patterns
Strategy: try-catch
Validate before calling
// a 200 without sid means the responder is not real Pi-hole v6 auth
if sid == "" {
return errors.New("got 200 but no session.sid — check proxy routing to /api/auth")
} Try / catch
if err != nil && strings.Contains(err.Error(), "empty session ID") {
// contract violation: log status+message embedded in the error and retry once
slog.Error("pihole auth contract violated", "error", err)
sid, err = fetchPiholeSessionID(url, client, password)
if err != nil {
return err
}
} Prevention
- Route /api/auth to the Pi-hole backend explicitly in proxy config.
- Keep Pi-hole on stable v6 releases; re-test auth after upgrades.
- Compare a direct (no-proxy) auth request against the proxied one when debugging.
When it happens
Trigger: A proxy or middleware returns its own 200 response (e.g. a health-check page) instead of proxying to the real /api/auth; a Pi-hole build/edge version with a changed auth payload; an empty JSON {} response.
Common situations: Reverse proxy misroute matching /api/auth to a static backend; unusual Pi-hole fork or beta; load balancer returning a synthetic 200 when the backend is down.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- fetching session ID: %v
- renewing session ID: %v
- authentication request returned status %s with message '%s'
- checking session ID: %v
- fetching stats: %v
AI-assisted analysis of glanceapp/glance@91324e8de7 (2026-08-15).
Data as JSON: /api/errors/086140efc8b685a8.
Report an issue: GitHub.