twentyhq/twenty · error · Error

Missing ${name} env var

Error message

Missing ${name} env var

What it means

The `purge-soft-deleted` maintenance script defines a `requireEnv` helper that reads a variable from `process.env` (loaded from `.env.local` or the file in `ENV_FILE`) and throws this generic message when one is unset. The script needs several configured values (Twenty GraphQL endpoint and an API key) to issue bulk-destroy operations against soft-deleted rows. Any missing one aborts before any destructive call is made.

Source

Thrown at packages/twenty-apps/internal/twenty-partners/src/scripts/purge-soft-deleted.ts:21

// Twenty SOFT-deletes (sets deletedAt); the row stays in the DB and keeps holding
// unique constraints (e.g. company domain, partner slug). But normal queries —
// including the import's existence checks — exclude soft-deleted rows. So after a
// UI "delete" or a partial import that got rolled back, re-running the import hits
// "A duplicate entry was detected" on records it cannot see. This purges those
// ghosts permanently so idempotent upserts work again.
//
// Only touches soft-deleted rows (deletedAt IS NOT NULL); active/default data is
// left untouched. One bulk destroy per object, so it is not rate-limited.
//
//   yarn purge            # against .env.local
//   yarn purge:prod       # against .env.prod
//
import { config } from 'dotenv';
config({ path: process.env.ENV_FILE ?? '.env.local' });

const requireEnv = (name: string): string => {
  const value = process.env[name];
  if (!value) throw new Error(`Missing ${name} env var`);
  return value;
};

// Objects the import writes to. partners + partnerContents are app custom objects;
// companies + opportunities are standard but populated by the import.
const OBJECTS = ['companies', 'partners', 'opportunities', 'partnerContents'] as const;

const gql = async (url: string, key: string, query: string): Promise<any> => {
  const response = await fetch(`${url.replace(/\/$/, '')}/graphql`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query }),
  });
  const json: any = await response.json();
  if (json.errors?.length) throw new Error(JSON.stringify(json.errors));
  return json.data;
};

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Open the `.env.local` (or `.env.prod` / the file in `ENV_FILE`) and confirm every variable the script reads via `requireEnv` is present and non-empty.
  2. Run with `ENV_FILE` explicitly set to the file you intend, e.g. `ENV_FILE=.env.prod yarn purge:prod`, to rule out a path mismatch.
  3. Check the script for the list of `requireEnv('...')` call sites and ensure each name matches your env file exactly (case-sensitive).
  4. If a key is genuinely optional in your setup, refactor `requireEnv` to a `getEnv` variant with a default rather than leaving it unset.

Example fix

// before
const requireEnv = (name: string): string => {
  const value = process.env[name];
  if (!value) throw new Error(`Missing ${name} env var`);
  return value;
};

// after — name the missing variable and the file that was loaded
const requireEnv = (name: string): string => {
  const value = process.env[name];
  if (!value) {
    throw new Error(
      `Missing ${name} env var (loaded from ${process.env.ENV_FILE ?? '.env.local'})`,
    );
  }
  return value;
};
Defensive patterns

Strategy: validation

Validate before calling

const requireEnv = (name: string): string => {
  const value = process.env[name];
  if (!value) {
    throw new Error(
      `Missing ${name} env var (loaded ${process.env.ENV_FILE ?? '.env.local'})`,
    );
  }
  return value;
};

// Fail fast at startup with all missing keys, not one at a time:
const REQUIRED = ['PARTNERS_API_URL', 'PARTNERS_API_KEY'] as const;
const missing = REQUIRED.filter((k) => !process.env[k]);
if (missing.length) {
  throw new Error(`Missing required env vars: ${missing.join(', ')}`);
}

Prevention

When it happens

Trigger: Running `yarn purge` / `yarn purge:prod` when one of the required env vars (e.g. the partners API base URL or the bearer API key) is absent from `.env.local` / `.env.prod`, or when `ENV_FILE` points at a file that does not exist or is missing keys.

Common situations: A freshly cloned repo without a populated `.env.local`, a typo in a variable name, pointing `ENV_FILE` at the wrong file, or copying a `.env.prod` that omits a key the script added in a newer revision.

Related errors


AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12). Data as JSON: /api/errors/b7571786d1a2934c. Report an issue: GitHub.