Repository navigation
Description
What happens
The CLI reads an env file line only when the variable name starts the line and = follows it directly (KEY=value, optionally with export). The loaders that frameworks use, such as dotenv in Next.js, accept more. When the CLI writes Clerk keys into a file whose existing Clerk line it doesn't recognize, it leaves that line in place and appends a second definition under a # Clerk header.
How the CLI reads each line shape today:
| Line | What the CLI does |
|---|---|
KEY=value |
reads it |
export KEY=value |
reads it |
KEY=value (indented) |
ignores it |
KEY = value (spaces around =) |
ignores it |
KEY=value in a file with Windows line endings (CRLF) |
ignores it |
KEY=value # note |
reads the value as value # note |
KEY= value |
reads the value as value, keeping the leading space |
In a file saved with Windows line endings, the CLI ignores every line.
Who hits it
Anyone whose env file holds Clerk keys in one of the ignored shapes, when they run:
clerk env pull, which writes keys into the file;clerk init, which writes keys through the same step and also writes its own route variables.
Windows line endings are the most likely trigger, for example a file created on Windows or copied through a Windows tool.
The CLI's other readers of env files skip the same lines, so they don't see those keys either: matching a project's key to an app in clerk link and clerk init, and the key checks in clerk doctor.
What the person experiences
- The command succeeds.
- The file now defines each Clerk key twice: the old line, then a new line under
# Clerk. - Which value the app uses depends on the framework's loader, so the old keys may still win.
clerk linkandclerk initmay fail to match the project to its app, because they don't see the key.
How big a deal it is
- How often: unknown. Windows line endings are the likeliest cause. Hand-formatted lines (indented or spaced) are rarer.
- How bad when it happens: low to medium. Nothing is lost, but the file is left with duplicate keys, and the app may keep running on the old ones.
- Does it block them: no.
Proposed fix
- Read env lines the way the frameworks' loaders do: allow leading whitespace and spaces around
=, strip a trailing\r, trim unquoted values, and drop a trailing# commentfrom unquoted values. Where loaders disagree, read conservatively. - When rewriting a key, replace the line the loader would read, keeping the file's other formatting and comments, instead of appending.
Where it goes: parseEnvFile and mergeEnvVars in packages/cli-core/src/lib/dotenv.ts. hasClerkKeys in packages/cli-core/src/commands/env/pull.ts uses its own, looser pattern and should share the same reader.
Tests
- Each line shape in the table above, read with the right value.
- Each shape rewritten in place, with no duplicate appended.
- A file with Windows line endings read and rewritten correctly.
Reproduce
- In a Next.js project, create
.env.localwith Windows line endings:
printf 'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_old\r\nCLERK_SECRET_KEY=sk_test_old\r\n' > .env.local - Link the project to an app and run
clerk env pull. - Open
.env.local. The original two lines are still there, followed by a blank line,# Clerk, and the two keys again.
Activity