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, err

View on GitHub (pinned to 91324e8de7)

Solutions

  1. curl -i -X POST {url}/api/auth and verify the body contains {"session":{"sid":"..."}}.
  2. Bypass the proxy temporarily (hit Pi-hole directly) to isolate where the 200-without-SID originates.
  3. Update/align the Pi-hole version to a stable v6 release if an edge build changed the payload.
  4. 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

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

Related errors


AI-assisted analysis of glanceapp/glance@91324e8de7 (2026-08-15). Data as JSON: /api/errors/086140efc8b685a8. Report an issue: GitHub.