Stirling-Tools/Stirling-PDF · error · Error
Failed to update seat count: ${error.message}
Error message
Failed to update seat count: ${error.message} What it means
Thrown by updateEnterpriseSeats when supabase.functions.invoke('manage-billing', {body:{new_seat_count,...}}) returns a non-null error. The manage-billing function creates a billing-portal session reflecting the requested seat change; a returned error means the function failed — invalid license key, the new seat count violates Stripe's minimums, no matching subscription, or missing STRIPE_SECRET_KEY. error.message is wrapped.
Source
Thrown at frontend/editor/src/proprietary/services/licenseService.ts:546
throw new Error(
"Supabase is not configured. Seat updates are not available.",
);
}
const baseUrl = window.location.origin;
const returnUrl = `${baseUrl}/settings/adminPlan?seats_updated=true`;
const { data, error } = await supabase.functions.invoke("manage-billing", {
body: {
return_url: returnUrl,
license_key: licenseKey,
self_hosted: true,
new_seat_count: newSeatCount,
},
});
if (error) {
throw new Error(`Failed to update seat count: ${error.message}`);
}
if (!data || !data.url) {
throw new Error("No billing portal URL returned");
}
return data.url;
},
};
/**
* Map license type to plan tier
* @param licenseInfo - Current license information
* @returns Plan tier: 'free' | 'server' | 'enterprise'
*/
export const mapLicenseToTier = (
licenseInfo: LicenseInfo | null,
): "free" | "server" | "enterprise" | null => {View on GitHub (pinned to 9ef20dcab8)
Solutions
- Read error.message — Stripe errors surface the constraint (e.g. minimum seats).
- Validate new_seat_count against the current seat minimum before calling.
- Confirm manage-billing is deployed with STRIPE_SECRET_KEY.
- Re-verify the license key is still active before retrying.
Example fix
// before
const url = await licenseService.updateEnterpriseSeats(newCount, key);
// after
try {
const url = await licenseService.updateEnterpriseSeats(newCount, key);
window.location.href = url;
} catch (e) {
showToast(e instanceof Error ? e.message : 'Seat update failed');
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!Number.isInteger(newSeatCount) || newSeatCount < currentMinSeats) {
setError(`Seat count must be at least ${currentMinSeats}`);
return;
}
if (!licenseKey) { setError('License key required'); return; } Type guard
export function isViableSeatCount(n: number, min: number): boolean {
return Number.isInteger(n) && n >= min;
} Try / catch
try {
const url = await licenseService.updateEnterpriseSeats(newCount, key);
window.location.href = url;
} catch (e) {
showToast(e instanceof Error ? e.message : 'Seat update failed');
} Prevention
- Validate new_seat_count against the subscription's minimum before calling.
- Confirm manage-billing is deployed with STRIPE_SECRET_KEY.
- Re-verify the license key is active before retrying.
When it happens
Trigger: new_seat_count below the subscription's minimum seats; license_key invalid/revoked; subscription not found in Stripe; manage-billing function missing secrets; proration rejected by Stripe.
Common situations: Admin tries to reduce seats below the active-user count; license was cancelled; Stripe product configured without per-seat pricing metadata; edge function not deployed.
Related errors
- No billing portal URL returned
- Failed to fetch plans: ${error.message}
- Failed to create checkout session: ${error.message}
- Failed to create billing portal session: ${error.message}
- Supabase is not configured. Seat updates are not available.
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/a790d2a0ceed08ad.
Report an issue: GitHub.