router-for-me/CLIProxyAPI · error

failed to create refresh request

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.

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.

Appendix: 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)