Search code examples
next.jsenvironment-variableszod

Zod throws error parsing environment variables in Nextjs 14


i am facing this error while parsing my env variables using zod in my nextjs 14 project:

ZodError: [
  {
    "code": "invalid_type",
    "expected": "string",
    "received": "undefined",
    "path": [
      "API_BASE_URL"
    ],
    "message": "Required"
  },
  {
    "code": "invalid_type",
    "expected": "string",
    "received": "undefined",
    "path": [
      "STRIPE_SECRET_KEY"
    ],
    "message": "Required"
  }
]


Source
lib/zod_schemas/serverEnvSchema.ts (8:29) @ parse

   6 | });
   7 |
>  8 | export const env = envSchema.parse({
     |                           ^
   9 | API_BASE_URL: process.env.API_BASE_URL,
  10 | STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
  11 | });

i am pretty sure the error is comming from CheckoutButton component below CheckoutButton.tsx(when i comment it out, the error dissapears):

"use client";

import { loadStripe } from "@stripe/stripe-js";
import { useRouter } from "next/navigation";

import { useGlobalContext } from "@/components/context/GlobalContext";

import checkout from "@/lib/checkout";

// import { env } from "@/lib/zod_schemas/clientEnvSchema";
import { Button } from "@/components/ui/button";

const stripePromise = loadStripe(
  process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!,
);

const CheckoutButton = () => {
  const { cartProducts } = useGlobalContext();
  const router = useRouter();

  const handleCheckout = async () => {
    try {
      const stripe = await stripePromise;
    //   checkout is a "use server" server action function i created in a separate file
    // which uses an env variable
      const { sessionId } = await checkout(cartProducts);

      const { error } = await stripe?.redirectToCheckout({
        sessionId,
      });

      if (error) {
        router.push("/error");
      }
    } catch (err) {
      console.error("Error in creating checkout session:", err);
      router.push("/error");
    }
  };

  return (
    <Button
      className="mt-6 w-full rounded-md bg-blue-500 py-1.5 font-medium text-blue-50 hover:bg-blue-600"
      onClick={handleCheckout}
    >
      Buy Now
    </Button>
  );
};

export default CheckoutButton;

this is my .env.local:

API_BASE_URL=someurl
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=somekey
STRIPE_SECRET_KEY=somekey

serverEnvSchema.ts

import zod from "zod";

const envSchema = zod.object({
  API_BASE_URL: zod.string().min(1),
  STRIPE_SECRET_KEY: zod.string().min(1),
});

export const env = envSchema.parse({
  API_BASE_URL: process.env.API_BASE_URL,
  STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
});

clientEnvSchema.ts

import zod from "zod";

const envSchema = zod.object({
  NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: zod.string().min(1),
});

export const env = envSchema.parse({
  NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY:
    process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
});

Before i had only one env file and that was API_BASE_URL and everything worked fine. this error appeared when i introduced the other 2 stripe variables.


What i tried

As you can see i separated my env variables into 2 different files, one containing the server secret variables and another containing the client variables. Before i had them all into one but that still hasnt fixed the issue. Also another thing is i tried calling the client public env variable using the env zod schema like this :

import { env } from "@/lib/zod_schemas/clientEnvSchema";

const stripePromise = loadStripe(
  env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
);

and like you normally would without zod:

// import { env } from "@/lib/zod_schemas/clientEnvSchema";

const stripePromise = loadStripe(
  process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!,
);

Either way did not solve the issue.

EDIT: the zod error only happens on "use client" client components. in server components i console logged the values and they are correct and no errors happen.


Solution

  • Hey I don't have the full answer for you but I hope this helps you as well in narrowing the problem the thing is the error you getting is a client component error so in full the client component is trying to access a var in the server side how i resolved this is by using this library https://env.t3.gg/docs/nextjs T3. instead of this code

    import zod from "zod";
    
    const envSchema = zod.object({
      API_BASE_URL: zod.string().min(1),
      STRIPE_SECRET_KEY: zod.string().min(1),
    });
    
    export const env = envSchema.parse({
      API_BASE_URL: process.env.API_BASE_URL,
      STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
    });

    adjust your file exactly like this before/after run pnpm add @t3-oss/env-nextjs zod or whatever package manager you are using

    import {z} from 'zod' 
    import {createEnv} from '@t3-oss/env-nextjs'
    
    
    export const env = createEnv ({
      server:{
             API_BASE_URL: zod.string().min(1),
             STRIPE_SECRET_KEY: zod.string().min(1),
             },
      client: { /*can be left empty or add the client-side variables*/ },
      runtimeEnv: { 
           API_BASE_URL: process.env.API_BASE_URL,
           STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
          /*Note everything you have in your server must also come here otherwise you'll get error of missing*/ 
           },
       });

    This did fix exactly my problem which is the same as yours I've been having but i find it strange cause everything was still working perfectly then out of the blue this happened to me