AlexxIT/go2rtc · error

failed to decode session response

Error message

failed to decode session response: %w

What it means

The Ring session creation request returned a non-2xx status; the response body is included for diagnostics. Prevents the client from establishing an authenticated session (needed for subsequent API calls).

Solutions

  1. Log the raw response body to see what was actually returned
  2. Update the ring client library to match the current API response schema
  3. Check for proxies/captive portals rewriting the response
  4. Inspect SessionResponse fields against the actual payload and adjust the struct

Example fix

null
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity-check raw body before decode:
// if !json.Valid(rawBody) { fail early }

Type guard

func looksLikeSession(b []byte) bool {
    var probe map[string]interface{}
    return json.Unmarshal(b, &probe) == nil
}

Try / catch

if err != nil {
    if strings.Contains(err.Error(), "failed to decode session response") {
        log.Printf("unexpected session payload; update library or check proxy")
    }
    return err
}

Prevention

When it happens

Trigger: Ring's session endpoint returns 2xx with a body that fails json.Decode into SessionResponse (schema drift, HTML error page behind a 200, truncated body).

Common situations: Ring API changes the session response shape; a proxy/captive portal returns a 200 HTML page; library version older than current Ring API contract.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07). Data as JSON: /api/errors/2e2d309486775622. Report an issue: GitHub.

Appendix: source

Thrown at pkg/ring/api.go:566

	req.Header.Set("Accept", "application/json")
	req.Header.Set("Authorization", "Bearer "+c.authToken.AccessToken)
	req.Header.Set("hardware_id", c.hardwareID)
	req.Header.Set("User-Agent", "android:com.ringapp")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		respBody, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("session request failed with status %d: %s", resp.StatusCode, string(respBody))
	}

	var sessionResp SessionResponse
	if err := json.NewDecoder(resp.Body).Decode(&sessionResp); err != nil {
		return fmt.Errorf("failed to decode session response: %w", err)
	}

	c.session = &sessionResp
	c.sessionExpiry = time.Now().Add(sessionValidTime)

	// Aktualisiere den gecachten Client
	cacheMutex.Lock()
	clientCache[c.cacheKey] = c
	cacheMutex.Unlock()

	return nil
}

func (c *RingApi) ensureAuth() error {
	c.authMutex.Lock()
	defer c.authMutex.Unlock()

	// If token exists and is not expired, use it

View on GitHub (pinned to c245815e75)