AlexxIT/go2rtc · error

failed to parse refresh token

Error message

failed to parse refresh token: %w

What it means

For RefreshTokenAuth, the factory parses the refresh token (which encodes an auth config JSON, including hardware ID) via parseAuthConfig. If the token is malformed, corrupted, or not the expected format, the underlying parse error is wrapped with this message. The token is not just an opaque string — it must decode into a valid config.

Solutions

  1. Regenerate a fresh refresh token using the library's official auth flow and use it verbatim.
  2. Trim whitespace/newlines from the token before parsing.
  3. Inspect the wrapped inner error (%w) to see whether it's a base64/JSON failure and fix that encoding.
  4. Ensure you are not passing an access token or password where the refresh token belongs.
  5. Store the token in a way that preserves it exactly (plain env var, single-quoted secret).

Example fix

// before
client, err := ring.NewClient(ring.RefreshTokenAuth{RefreshToken: strings.TrimSpace(yamlToken)}) // token was base64 of the real token
// after
tok, _ := base64.StdEncoding.DecodeString(yamlToken)
client, err := ring.NewClient(ring.RefreshTokenAuth{RefreshToken: string(tok)})
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity-check the token decodes before calling NewClient
if _, err := parseAuthConfig(rt); err != nil { return fmt.Errorf("bad refresh token: %w", err) }

Try / catch

client, err := ring.NewClient(ring.RefreshTokenAuth{RefreshToken: rt})
if err != nil {
    var parseErr error
    if errors.As(err, &parseErr) && strings.Contains(err.Error(), "failed to parse refresh token") {
        return fmt.Errorf("regenerate the Ring refresh token via the auth flow: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Passing a truncated/corrupted refresh token; passing some other secret (e.g. an OAuth access token or a password) in place of a Ring refresh token; a token that was hand-edited or line-wrapped when copied.

Common situations: Copy/paste damage (whitespace/newlines introduced); regenerating tokens in the Ring app invalidates old-format tokens; storing the token through a system that mangles it (YAML quoting, base64 double-encoding).

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/8dad2c986340abad. Report an issue: GitHub.

Appendix: source

Thrown at pkg/ring/api.go:198

		if cachedClient.authToken != nil && time.Now().Before(cachedClient.tokenExpiry) {
			cachedClient.onTokenRefresh = onTokenRefresh
			return cachedClient, nil
		}
	}

	client := &RingApi{
		httpClient:     &http.Client{Timeout: defaultTimeout},
		onTokenRefresh: onTokenRefresh,
		hardwareID:     generateHardwareID(),
		auth:           auth,
		cacheKey:       cacheKey,
	}

	switch a := auth.(type) {
	case RefreshTokenAuth:
		config, err := parseAuthConfig(a.RefreshToken)
		if err != nil {
			return nil, fmt.Errorf("failed to parse refresh token: %w", err)
		}

		client.authConfig = config
		client.hardwareID = config.HID
		client.RefreshToken = a.RefreshToken
	}

	clientCache[cacheKey] = client

	return client, nil
}

func ClientAPI(path string) string {
	return clientAPIBaseURL + path
}

func DeviceAPI(path string) string {
	return deviceAPIBaseURL + path

View on GitHub (pinned to c245815e75)