AlexxIT/go2rtc · critical

nest: invalid cache key format

Error message

nest: invalid cache key format

What it means

In refreshToken (pkg/nest/api.go:249), the cached refresh key is split on ':' and must yield exactly 3 parts: clientID, clientSecret, refreshToken. The library throws this error when the cached credential string does not have that clientID:clientSecret:refreshToken shape, because it cannot reconstruct the OAuth credentials needed to call NewAPI and refresh the token.

Solutions

  1. Clear the credential cache and re-authenticate so the key is written in the current clientID:clientSecret:refreshToken format.
  2. Verify the cached value contains exactly two ':' separators and that clientSecret/refreshToken contain no raw ':' (URL-encode or re-store them if they do).
  3. Check how the key was stored: ensure the code path that caches credentials uses the same 3-field format that refreshToken expects.
  4. Upgrade/downgrade the library so the writer and reader of the cache key agree on the format.

Example fix

// before: storing a 2-field key
client.Set("nest creds", clientID + ":" + refreshToken)
// after: store all three fields, colon-free
if strings.ContainsAny(clientSecret+refreshToken, ":") { /* re-encode or error */ }
client.Set("nest creds", clientID + ":" + clientSecret + ":" + refreshToken)
Defensive patterns

Strategy: validation

Validate before calling

func validNestCacheKey(key string) bool {
    parts := strings.Split(key, ":")
    return len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != "" &&
        !strings.ContainsAny(parts[1]+parts[2], ":")
}
if !validNestCacheKey(refreshKey) { return fmt.Errorf("malformed nest cache key") }

Type guard

func isNestCacheKey(v any) (clientID, clientSecret, refreshToken string, ok bool) {
    s, ok := v.(string)
    if !ok { return "", "", "", false }
    parts := strings.Split(s, ":")
    if len(parts) != 3 { return "", "", "", false }
    return parts[0], parts[1], parts[2], true
}

Try / catch

creds, err := loadCachedCreds(userID)
if err != nil || strings.Count(creds, ":") != 2 {
    // re-authenticate instead of calling ExchangeSDP
    return reauthenticate(ctx, userID)
}

Prevention

When it happens

Trigger: ExchangeSDP calls refreshToken with a refreshKey whose strings.Split(key, ":") returns != 3 parts — e.g. the key was cached with only 2 fields, with extra colons, or as an empty/malformed string.

Common situations: A credential cache entry written by an older library version with a different key layout; a client secret or refresh token that itself contains a ':' corrupting the field count; hand-edited or truncated cache storage (Redis/file); restoring cache values from a different integration.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at pkg/nest/api.go:249

	// Get the cached API with matching token to get credentials
	var refreshKey string
	cacheMu.Lock()
	for key, api := range cache {
		if api.Token == a.Token {
			refreshKey = key
			break
		}
	}
	cacheMu.Unlock()

	if refreshKey == "" {
		return errors.New("nest: unable to find cached credentials")
	}

	// Parse credentials from cache key
	parts := strings.Split(refreshKey, ":")
	if len(parts) != 3 {
		return errors.New("nest: invalid cache key format")
	}
	clientID, clientSecret, refreshToken := parts[0], parts[1], parts[2]

	// Get new API instance which will refresh the token
	newAPI, err := NewAPI(clientID, clientSecret, refreshToken)
	if err != nil {
		return err
	}

	// Update current API with new token
	a.Token = newAPI.Token
	a.ExpiresAt = newAPI.ExpiresAt
	return nil
}

func (a *API) ExtendStream() error {
	var reqv struct {
		Command string `json:"command"`

View on GitHub (pinned to c245815e75)