[Stripe Plugin] authClient.subscription.list() returns unexpected 404 response #2025

Closed
opened 2026-03-13 09:21:36 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @MickeyKay on GitHub (Sep 26, 2025).

@dosu I have the following code:

'use client';

import { useEffect, useState } from 'react';
import { Button } from '@/components/ui/button';
import { authClient } from '@/lib/auth-client';
import { useRouter } from 'next/navigation';
import { toast } from 'sonner';

export function SubscribeButton({ plan, isSignedIn }: { plan: string; isSignedIn: boolean }) {
  const router = useRouter();
  const { data: activeOrganization } = authClient.useActiveOrganization();
  const [subscriptions, setSubscriptions] = useState<any[] | null>(null);

  useEffect(() => {
    async function loadSubscriptions() {
      if (!activeOrganization?.id) return;

      const { data, error } = await authClient.subscription.list({
        referenceId: activeOrganization.id,
      });

      if (error) {
        console.error('[SubscribeButton] error loading subscriptions:', error);
      } else {
        setSubscriptions(data ?? []);
      }
    }

    loadSubscriptions();
  }, [activeOrganization?.id]);

  useEffect(() => {
    if (subscriptions) {
      console.log('[SubscribeButton] subscriptions:', subscriptions);
    }
  }, [subscriptions]);

  let flowError;

  async function triggerSubscriptionFlow() {
    if (!isSignedIn) {
      router.push('/auth/sign-up?redirectTo=/pricing');
      return;
    }

    try {
      const { data, error } = await authClient.subscription.upgrade({
        plan,
        referenceId: activeOrganization?.id,
        successUrl: '/settings/plan-billing',
        cancelUrl: '/pricing',
      });

      if (error) flowError = error;
      if (data) console.log('[triggerSubscriptionFlow] data:', data);
    } catch (err) {
      flowError = err;
    }

    if (flowError) {
      toast.error('You are not authorized to update the subscription plan.');
    }
  }

  return (
    <Button variant="healthy" size="xl" onClick={triggerSubscriptionFlow}>
      Get Started
    </Button>
  );
}

The call to authClient.subscription.list() is returning a 404, despite the fact that I have logged and verified that activeOrganization.id matches the reference_id column for the expected subscription in my database, and I am logged in as the user to which the subscription's stripe_customer_id is associated.

Any idea why this might be?

@dosu Do you see anything obviously wrong with this implementation?

Originally created by @MickeyKay on GitHub (Sep 26, 2025). @dosu I have the following code: ``` 'use client'; import { useEffect, useState } from 'react'; import { Button } from '@/components/ui/button'; import { authClient } from '@/lib/auth-client'; import { useRouter } from 'next/navigation'; import { toast } from 'sonner'; export function SubscribeButton({ plan, isSignedIn }: { plan: string; isSignedIn: boolean }) { const router = useRouter(); const { data: activeOrganization } = authClient.useActiveOrganization(); const [subscriptions, setSubscriptions] = useState<any[] | null>(null); useEffect(() => { async function loadSubscriptions() { if (!activeOrganization?.id) return; const { data, error } = await authClient.subscription.list({ referenceId: activeOrganization.id, }); if (error) { console.error('[SubscribeButton] error loading subscriptions:', error); } else { setSubscriptions(data ?? []); } } loadSubscriptions(); }, [activeOrganization?.id]); useEffect(() => { if (subscriptions) { console.log('[SubscribeButton] subscriptions:', subscriptions); } }, [subscriptions]); let flowError; async function triggerSubscriptionFlow() { if (!isSignedIn) { router.push('/auth/sign-up?redirectTo=/pricing'); return; } try { const { data, error } = await authClient.subscription.upgrade({ plan, referenceId: activeOrganization?.id, successUrl: '/settings/plan-billing', cancelUrl: '/pricing', }); if (error) flowError = error; if (data) console.log('[triggerSubscriptionFlow] data:', data); } catch (err) { flowError = err; } if (flowError) { toast.error('You are not authorized to update the subscription plan.'); } } return ( <Button variant="healthy" size="xl" onClick={triggerSubscriptionFlow}> Get Started </Button> ); } ``` The call to `authClient.subscription.list()` is returning a 404, despite the fact that I have logged and verified that `activeOrganization.id` matches the `reference_id` column for the expected subscription in my database, and I am logged in as the user to which the subscription's `stripe_customer_id` is associated. Any idea why this might be? @dosu Do you see anything obviously wrong with this implementation?
GiteaMirror added the nextjsbug labels 2026-03-13 09:21:36 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Sep 26, 2025):

A 404 from authClient.subscription.list() is almost never due to business logic—if no subscriptions are found, it returns an empty array, not a 404. The most common causes are:

  • The Stripe plugin is not enabled with subscription: true (or is misconfigured, e.g., using subscriptions instead of subscription) in your plugin setup. Double-check your plugin config matches the docs—this is a frequent source of 404s for these endpoints [source].
  • The /subscription/list endpoint is disabled or not registered, possibly due to disabledPaths or a plugin conflict [source].
  • The client is not sending a valid session/cookie with the request. If the session is missing, some setups (especially with custom proxies or serverless) can return a 404 instead of a 401 [source].
  • The referenceId must be sent as a query param, not in the body. The client should call:
    authClient.subscription.list({ referenceId: activeOrganization.id })
    
    which matches your code, but if you have a custom client wrapper, confirm it sends referenceId as a query param [source].

Your component looks fine for how it calls the API. The issue is almost certainly in plugin config, endpoint registration, or session propagation. Double-check the plugin config, ensure the endpoint is not disabled, and verify the session is present in the request. If you have custom authorizeReference logic, make sure it doesn't throw or block access for this user/referenceId (though this should return 401, not 404).

If you want to share your plugin config or server logs, I can help you pinpoint the misconfiguration.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 26, 2025): <!-- Answer --> A 404 from `authClient.subscription.list()` is almost never due to business logic—if no subscriptions are found, it returns an empty array, not a 404. The most common causes are: - The Stripe plugin is not enabled with `subscription: true` (or is misconfigured, e.g., using `subscriptions` instead of `subscription`) in your plugin setup. Double-check your plugin config matches the docs—this is a frequent source of 404s for these endpoints [[source]](https://github.com/better-auth/better-auth/issues/4801). - The `/subscription/list` endpoint is disabled or not registered, possibly due to `disabledPaths` or a plugin conflict [[source]](https://github.com/better-auth/better-auth/blob/f6cbdcc84ee5d2971fdcc8b23ff7c174f88cf45b/packages/better-auth/src/api/index.ts). - The client is not sending a valid session/cookie with the request. If the session is missing, some setups (especially with custom proxies or serverless) can return a 404 instead of a 401 [[source]](https://github.com/better-auth/better-auth/issues/3745). - The `referenceId` must be sent as a query param, not in the body. The client should call: ```js authClient.subscription.list({ referenceId: activeOrganization.id }) ``` which matches your code, but if you have a custom client wrapper, confirm it sends `referenceId` as a query param [[source]](https://github.com/better-auth/better-auth/blob/f6cbdcc84ee5d2971fdcc8b23ff7c174f88cf45b/packages/stripe/src/index.ts). Your component looks fine for how it calls the API. The issue is almost certainly in plugin config, endpoint registration, or session propagation. Double-check the plugin config, ensure the endpoint is not disabled, and verify the session is present in the request. If you have custom `authorizeReference` logic, make sure it doesn't throw or block access for this user/referenceId (though this should return 401, not 404). If you want to share your plugin config or server logs, I can help you pinpoint the misconfiguration. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/b0167be0-d8f9-40d1-8cee-b0b2215182e5?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/b0167be0-d8f9-40d1-8cee-b0b2215182e5?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/b0167be0-d8f9-40d1-8cee-b0b2215182e5?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/b0167be0-d8f9-40d1-8cee-b0b2215182e5?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/b0167be0-d8f9-40d1-8cee-b0b2215182e5?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/b0167be0-d8f9-40d1-8cee-b0b2215182e5?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/b0167be0-d8f9-40d1-8cee-b0b2215182e5?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4933)
Author
Owner

@MickeyKay commented on GitHub (Sep 26, 2025):

@dosu Ah, there's an error in the docs! The code example from https://www.better-auth.com/docs/plugins/stripe#listing-active-subscriptions is:

const { data: subscriptions, error } = await authClient.subscription.list({
    referenceId: '123',
});

. . . but should be:

const { data: subscriptions, error } = await authClient.subscription.list({
    query: {
        referenceId: '123',
    }
});
@MickeyKay commented on GitHub (Sep 26, 2025): @dosu Ah, there's an error in the docs! The code example from https://www.better-auth.com/docs/plugins/stripe#listing-active-subscriptions is: ``` const { data: subscriptions, error } = await authClient.subscription.list({ referenceId: '123', }); ``` . . . but should be: ``` const { data: subscriptions, error } = await authClient.subscription.list({ query: { referenceId: '123', } });
Author
Owner

@dosubot[bot] commented on GitHub (Sep 26, 2025):

Great catch—you're absolutely right. The correct usage for listing subscriptions with the Stripe plugin is to pass the referenceId inside a query object, like this:

authClient.subscription.list({
  query: { referenceId: '123' }
});

Passing referenceId directly (not inside query) causes the endpoint to miss the parameter, which can lead to unexpected 404 errors because the schema expects it nested under query [source]. This was recently clarified in the code and schema validation, but the docs example hasn't been updated yet.

If anyone else runs into unexplained 404s with this endpoint, double-check that parameters are structured as { query: { referenceId } }—this matches both the API contract and the latest implementation [source].

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 26, 2025): <!-- Answer --> Great catch—you're absolutely right. The correct usage for listing subscriptions with the Stripe plugin is to pass the `referenceId` inside a `query` object, like this: ```js authClient.subscription.list({ query: { referenceId: '123' } }); ``` Passing `referenceId` directly (not inside `query`) causes the endpoint to miss the parameter, which can lead to unexpected 404 errors because the schema expects it nested under `query` [[source](https://github.com/better-auth/better-auth/pull/3437)]. This was recently clarified in the code and schema validation, but the docs example hasn't been updated yet. If anyone else runs into unexplained 404s with this endpoint, double-check that parameters are structured as `{ query: { referenceId } }`—this matches both the API contract and the latest implementation [[source](https://github.com/better-auth/better-auth/issues/4267)]. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/aa30aa4e-7fd6-4a03-9c29-9f6186eb8946?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/aa30aa4e-7fd6-4a03-9c29-9f6186eb8946?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/aa30aa4e-7fd6-4a03-9c29-9f6186eb8946?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/aa30aa4e-7fd6-4a03-9c29-9f6186eb8946?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/aa30aa4e-7fd6-4a03-9c29-9f6186eb8946?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/aa30aa4e-7fd6-4a03-9c29-9f6186eb8946?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/aa30aa4e-7fd6-4a03-9c29-9f6186eb8946?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4933)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2025