Home › Guides › Stripe current_period_end is undefined

Stripe subscription current_period_end is undefined: what changed in 2025-03-31.basil

Matching pack: Subscription Billing Kit (for Stripe) (US$19), an Agent Skill for Claude Code and Cursor with a webhook-driven access model for SaaS subscriptions and version-tolerant readers for these fields. Read its test report (12 unit tests plus 3 request-schema checks against stripe-mock, not real Stripe test mode). Free: Stripe basil field checker, which runs in your browser and lists every moved field in a pasted event.

You upgraded the Stripe SDK or your account's API version, and now subscription.current_period_end is undefined, or TypeScript says the property doesn't exist on Subscription. Renewal dates and "access until" checks quietly break. The field wasn't lost: Stripe moved it in API version 2025-03-31.basil.

The short answer

Sources: Stripe changelog, 2025-03-31.basil, "Adds subscription item-level billing periods and removes subscription-level periods" (docs.stripe.com/changelog/basil/2025-03-31/deprecate-subscription-current-period-start-and-end); Stripe changelog, 2025-03-31.basil, "Invoicing resources now specify how they were generated" (docs.stripe.com/changelog/basil/2025-03-31/adds-new-parent-field-to-invoicing-objects). Checked 24 Oct 2026.

Why it shows up after an SDK upgrade

Stripe's typed SDKs pin an API version. Stripe's changelog lists the first SDK majors for basil, for example stripe-node v18.0.0, stripe-python v12.0.0, stripe-php v17.0.0, stripe-java v29.0.0, stripe-go v82.0.0 and stripe-dotnet v48.0.0. Upgrading to one of those (or later) means your API calls return the new shape, even if nothing else in your code changed.

Webhooks can still send the old shape

Webhook payloads follow the API version of the webhook endpoint, not your SDK. So after an SDK upgrade, stripe.subscriptions.retrieve() can return the new shape while customer.subscription.updated events still arrive in the old one, until you upgrade the endpoint's version too. Check which API version the endpoint uses in the Dashboard. Stripe's upgrade steps list upgrading webhook endpoints as its own step.

Code that reads both shapes

These are the same helpers our Subscription Billing Kit uses. They read the new location first and fall back to the old one:

// Reads both API shapes: before and after 2025-03-31.basil.
import type Stripe from "stripe";

/** Latest billing-period end (Unix seconds) across the subscription's items. */
export function periodEnd(sub: Stripe.Subscription): number | null {
  const itemEnds = (sub.items?.data ?? [])
    .map((i: any) => i.current_period_end)                // basil and later
    .filter((n: unknown): n is number => typeof n === "number");
  if (itemEnds.length) return Math.max(...itemEnds);
  const legacy = (sub as any).current_period_end;         // before basil
  return typeof legacy === "number" ? legacy : null;
}

/** The subscription id an invoice belongs to, or null for a one-off invoice. */
export function invoiceSubscriptionId(inv: Stripe.Invoice): string | null {
  const v = (inv as any).parent?.subscription_details?.subscription  // basil and later
         ?? (inv as any).subscription                                 // before basil
         ?? null;
  return typeof v === "string" ? v : v?.id ?? null;
}

Items on one subscription can have different billing periods (for example a monthly and a yearly price). periodEnd above takes the latest end, which suits an "access until" date. If you bill per item, read the period from the item you care about instead.

Other basil changes worth checking at the same time

Sources: Stripe changelog, 2025-03-31.basil, "Adds subscription item-level billing periods and removes subscription-level periods" (docs.stripe.com/changelog/basil/2025-03-31/deprecate-subscription-current-period-start-and-end); Stripe changelog, 2025-03-31.basil, "Invoicing resources now specify how they were generated" (docs.stripe.com/changelog/basil/2025-03-31/adds-new-parent-field-to-invoicing-objects). Checked 24 Oct 2026.

Let your agent build it the safe way

Subscription Billing Kit (for Stripe) (US$19) is an Agent Skill for Claude Code and Cursor. It gives your agent a webhook-driven access model for SaaS: Checkout in subscription mode, the Customer Portal, an access policy for all 8 subscription statuses, protection against out-of-order events, trials, cancel-at-period-end, failed-payment hooks, and helpers for Stripe's 2025 "Basil" API changes. Its tests are 12 unit tests with basil-shaped fixtures plus 3 request-schema checks against stripe-mock; none run against real Stripe test mode. It assumes webhook delivery is already safe: Webhooks Done Right (for Stripe) (US$19) covers raw body, signatures and duplicate events.

See Subscription Billing Kit (for Stripe) · Read its test report