router-for-me/CLIProxyAPI · error

missing access_token and refresh_token

Error message

missing access_token and refresh_token

What it means

In cmd/fetch_codex_models/main.go the tool resolves an access token from an auth file's metadata. If metadata contains neither a usable `access_token` nor a `refresh_token`, there is no credential to present or refresh, so it fails with `missing access_token and refresh_token`. This always indicates the selected auth file is not a completed Codex OAuth credential (or the keys are named differently).

Source

Thrown at cmd/fetch_codex_models/main.go:194

		return auth
	}
	return nil
}

func ensureAccessToken(ctx context.Context, store *sdkauth.FileTokenStore, auth *coreauth.Auth) (string, bool, error) {
	accessToken := metaStringValue(auth.Metadata, "access_token")
	if accessToken != "" {
		if expiresAt, ok := auth.ExpirationTime(); !ok || time.Now().Add(accessTokenRefreshLeeway).Before(expiresAt) {
			return accessToken, false, nil
		}
	}

	refreshToken := metaStringValue(auth.Metadata, "refresh_token")
	if refreshToken == "" {
		if accessToken != "" {
			return accessToken, false, nil
		}
		return "", false, fmt.Errorf("missing access_token and refresh_token")
	}

	svc := codexauth.NewCodexAuthWithProxyURL(nil, auth.ProxyURL)
	tokenData, errRefresh := svc.RefreshTokensWithRetry(ctx, refreshToken, 3)
	if errRefresh != nil {
		return "", false, errRefresh
	}
	if strings.TrimSpace(tokenData.AccessToken) == "" {
		return "", false, fmt.Errorf("refresh response did not include access_token")
	}

	if auth.Metadata == nil {
		auth.Metadata = make(map[string]any)
	}
	auth.Metadata["id_token"] = tokenData.IDToken
	auth.Metadata["access_token"] = tokenData.AccessToken
	if tokenData.RefreshToken != "" {
		auth.Metadata["refresh_token"] = tokenData.RefreshToken

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Re-authenticate Codex via the proxy's OAuth login flow so auths/<file>.json contains a full metadata block with access_token/refresh_token.
  2. Verify the JSON structure: tokens must live under `metadata.access_token` / `metadata.refresh_token`, not at the top level.
  3. Confirm the auth file type is codex (`metadata.type: "codex"`) and not another provider's credential.
  4. Point --auth at the correct file in auths/ generated by a successful login.

Example fix

// before: incomplete auth file
{ "id": "my-auth", "type": "codex" }

// after: complete auth file
{
  "id": "my-auth",
  "type": "codex",
  "metadata": {
    "access_token": "...",
    "refresh_token": "...",
    "type": "codex"
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// Run before invoking the tool/SDK with an auth file
func authHasCodexTokens(authJSON []byte) bool {
    meta := gjson.GetBytes(authJSON, "metadata")
    return meta.Get("access_token").String() != "" || meta.Get("refresh_token").String() != ""
}

Type guard

func isUsableCodexAuth(meta map[string]any) bool {
    at, _ := meta["access_token"].(string)
    rt, _ := meta["refresh_token"].(string)
    return strings.TrimSpace(at) != "" || strings.TrimSpace(rt) != ""
}

Prevention

When it happens

Trigger: Running fetch_codex_models with --auth pointing at a JSON file whose metadata lacks both `access_token` and `refresh_token` keys: a hand-written stub file, a partially completed login, an auth file of a different provider type (e.g. Gemini/Qwen), or metadata keys misspelled.

Common situations: Reusing an auths/ file from another provider; login flow interrupted before tokens were persisted; manually edited auth JSON that dropped the metadata block; file-level key expected at top level instead of under `metadata`.

Related errors


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