TryGhost/Ghost · error · Error
Invalid donation amount: ${value}
Error message
Invalid donation amount: ${value} What it means
FakeCheckoutPage.getAmountInCents() reads the custom amount input, strips non-numeric characters with /[^0-9.]/g, and parses with Number.parseFloat. If the result is not finite (NaN, Infinity) it throws, embedding the raw input value. This guards against an empty or purely-symbolic amount field — the Stripe fake checkout couldn't surface a usable number.
Source
Thrown at e2e/helpers/pages/stripe/fake-checkout-page.ts:59
async fillEmail(email: string): Promise<void> {
await this.emailInput.fill(email);
}
async getEmail(): Promise<string> {
return await this.emailInput.inputValue();
}
async getAmountInCents(): Promise<number> {
if (!await this.customAmountInput.isVisible()) {
await this.changeAmountButton.click();
}
const value = await this.customAmountInput.inputValue();
const normalizedValue = value.replace(/[^0-9.]/g, '');
const parsed = Number.parseFloat(normalizedValue);
if (!Number.isFinite(parsed)) {
throw new Error(`Invalid donation amount: ${value}`);
}
return Math.round(parsed * 100);
}
async submitPayment(): Promise<void> {
await this.submitButton.click();
}
}
View on GitHub (pinned to 47d8b0e2ad)
Solutions
- Ensure a donation amount is selected/typed before calling getAmountInCents().
- Log the raw value to see what was read; update the selector if it grabs the wrong input.
- Broaden the normalization (handle comma decimal separators) before parsing.
- Assert the input is visible and populated before reading.
Example fix
// before
const value = await this.customAmountInput.inputValue();
const parsed = Number.parseFloat(value.replace(/[^0-9.]/g, ''));
if (!Number.isFinite(parsed)) throw new Error(`Invalid donation amount: ${value}`);
// after
const value = (await this.customAmountInput.inputValue()).trim();
const normalized = value.replace(/[^0-9.,]/g, '').replace(',', '.');
const parsed = Number.parseFloat(normalized);
if (!Number.isFinite(parsed) || parsed <= 0) {
throw new Error(`Invalid donation amount: ${JSON.stringify(value)}`);
} Defensive patterns
Strategy: validation
Validate before calling
const value = (await this.customAmountInput.inputValue()).trim();
if (!value) throw new Error('customAmountInput is empty — set an amount before reading'); Type guard
function isFinitePositiveAmount(raw: string): boolean {
const normalized = raw.replace(/[^0-9.,]/g, '').replace(',', '.');
const n = Number.parseFloat(normalized);
return Number.isFinite(n) && n > 0;
} Try / catch
const value = await this.customAmountInput.inputValue();
const normalized = value.replace(/[^0-9.,]/g, '').replace(',', '.');
const parsed = Number.parseFloat(normalized);
if (!Number.isFinite(parsed) || parsed <= 0) {
throw new Error(`Invalid donation amount: ${JSON.stringify(value)}`);
}
return Math.round(parsed * 100); Prevention
- Set/select an amount before reading the field.
- Handle locale-specific decimal separators (comma) in normalization.
- Assert the input is visible and populated before parsing.
When it happens
Trigger: The customAmountInput is empty when read. The input contains only currency symbols/spaces that all get stripped, leaving ''. Value is 'Infinity' or non-numeric placeholder text. The changeAmountButton click didn't reveal the input as expected.
Common situations: Test navigated to checkout before an amount was set; Stripe fake page DOM changed so the selector reads the wrong element; amount formatted in a locale that the regex strips entirely.
Related errors
- Latest Stripe checkout session does not include a success UR
- Latest Stripe checkout session does not include a success UR
- Latest Stripe checkout session does not include a success UR
- Latest Stripe checkout session does not include a success UR
- Could not create Stripe checkout session
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/be9fbf68a268d5a7.
Report an issue: GitHub.