router-for-me/CLIProxyAPI · error

failed to create refresh request: %w

Error message

failed to create refresh request: %w

What it means

http.NewRequestWithContext failed while building the refresh-token POST in refreshTokensSingleFlight. Like the exchange path, method and TokenURL are hardcoded, so a failure here practically means the passed context is nil or already canceled — not a network problem. Distinguished from 207 by occurring before any socket is opened.

Source

Thrown at internal/auth/codex/openai_auth.go:223

	}
	tokenData, ok := result.(*CodexTokenData)
	if !ok || tokenData == nil {
		return nil, fmt.Errorf("token refresh failed: invalid single-flight result")
	}
	return tokenData, nil
}

func (o *CodexAuth) refreshTokensSingleFlight(ctx context.Context, refreshToken string) (*CodexTokenData, error) {
	data := url.Values{
		"client_id":     {ClientID},
		"grant_type":    {"refresh_token"},
		"refresh_token": {refreshToken},
		"scope":         {"openid profile email"},
	}

	req, errReq := http.NewRequestWithContext(ctx, "POST", TokenURL, strings.NewReader(data.Encode()))
	if errReq != nil {
		return nil, fmt.Errorf("failed to create refresh request: %w", errReq)
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Accept", "application/json")

	resp, errDo := o.httpClient.Do(req)
	if errDo != nil {
		return nil, fmt.Errorf("token refresh request failed: %w", errDo)
	}
	defer func() {
		if errClose := resp.Body.Close(); errClose != nil {
			log.Errorf("token refresh response body close error: %v", errClose)
		}
	}()

	body, errRead := io.ReadAll(resp.Body)
	if errRead != nil {
		return nil, fmt.Errorf("failed to read refresh response: %w", errRead)

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Check for prior context cancellation with errors.Is(err, context.Canceled) on the wrapped error.
  2. Give the refresh path its own short-lived context derived from Background, not one tied to request/shutdown lifetimes.
  3. If embedding via sdk/cliproxy, verify the context passed into token refresh calls is non-nil.

Example fix

// before
ctx := requestCtx // canceled when the request is done
_, _ = auth.RefreshTokensWithRetry(ctx, token, 3)

// after
refreshCtx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
_, _ = auth.RefreshTokensWithRetry(refreshCtx, token, 3)
Defensive patterns

Strategy: validation

Validate before calling

if ctx == nil || ctx.Err() != nil {
    ctx = context.Background() // or return an explicit error
}
_, err := auth.RefreshTokens(ctx, refreshToken)

Try / catch

td, err := auth.RefreshTokens(ctx, rt)
if err != nil {
    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
        // request-building failure from a dead context; retry with a fresh one
    }
}

Prevention

When it happens

Trigger: Refresh attempted with a canceled context (shutdown in progress); nil context reaching this function through a custom integration; modified TokenURL constant.

Common situations: Config hot-reload or process shutdown canceling the parent context exactly as a background refresh fires; SDK embedders passing the wrong context into the refresh path.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/040551db952d7b2e. Report an issue: GitHub.