can1357/oh-my-pi · error · AIError.ProviderHttpError

Invalid Copilot billing usage response

Error message

Invalid Copilot billing usage response

What it means

Thrown by fetchBillingUsage when the GitHub Copilot billing usage endpoint returns 200 but the body is not a JSON object, so it cannot be treated as BillingUsageResponse. Like the internal-user variant, the library checks isRecord(data) and throws ProviderHttpError with status 200 to indicate a malformed payload rather than an HTTP failure.

Source

Thrown at packages/ai/src/usage/github-copilot.ts:211

	baseUrl: string,
	username: string,
	token: string,
	signal?: AbortSignal,
): Promise<BillingUsageResponse> {
	const data = await fetchJson(
		ctx,
		`${baseUrl}/users/${encodeURIComponent(username)}/settings/billing/premium_request/usage`,
		{
			headers: {
				Accept: "application/vnd.github+json",
				Authorization: `Bearer ${token}`,
				"X-GitHub-Api-Version": "2022-11-28",
			},
			signal,
		},
	);

	if (!isRecord(data)) throw new AIError.ProviderHttpError("Invalid Copilot billing usage response", 200);
	return data as BillingUsageResponse;
}

function buildLimitFromQuota(
	key: string,
	label: string,
	quota: CopilotQuotaDetail,
	plan: string,
	window: UsageWindow | undefined,
	accountId?: string,
): UsageLimit {
	const used = quota.unlimited ? undefined : Math.max(0, quota.entitlement - quota.remaining);
	const limit = quota.unlimited ? undefined : quota.entitlement;
	const amount = buildAmount(used, limit, "requests");
	const notes: string[] = [];
	if (quota.unlimited) notes.push("Unlimited");
	if (quota.overage_count > 0) {
		notes.push(`Overage requests: ${quota.overage_count}`);

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect the raw 200 body to identify what the endpoint actually returned.
  2. Confirm the base URL matches GitHub's billing API for the account type (github.com vs GHE with matching endpoint).
  3. Bypass or correctly configure proxies that could inject HTML responses.
  4. Upgrade the library if GitHub altered the billing usage response schema.

Example fix

// before: pointing billing fetch at GHE host lacking this endpoint
const data = await fetchBillingUsage(ctx, "https://ghe.corp.example/api", ...);
// after: use the public GitHub billing API
const data = await fetchBillingUsage(ctx, "https://api.github.com", ...);
Defensive patterns

Strategy: type-guard

Type guard

function isBillingUsage(v: unknown): v is Record<string, unknown> {
	return typeof v === "object" && v !== null && !Array.isArray(v) && "quota_snapshots" in v;
}

Try / catch

try {
	const billing = await provider.billing(params);
} catch (err) {
	if (err instanceof AIError.ProviderHttpError && /Invalid Copilot billing usage response/.test(err.message)) {
		// capture raw body upstream / fall back to cached report
	} else throw err;
}

Prevention

When it happens

Trigger: A successful (200) response from the billing usage endpoint whose parsed JSON is not an object — HTML from a proxy, an array, null, or a changed API response schema.

Common situations: Enterprise proxy or API gateway serving an interstitial page; GHE (GitHub Enterprise) base URL whose billing endpoint differs from github.com's; upstream API contract change on the billing endpoint.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/3ff78051ed940561. Report an issue: GitHub.