Skip to content

Fix variable interpolation in .env files loaded through envFile - #14828

Open
Abdelhadi Salmaoui (abdelhadi703) wants to merge 2 commits into
microsoft:mainfrom
abdelhadi703:fix/envfile-interpolation
Open

Abdelhadi Salmaoui (abdelhadi703) wants to merge 2 commits into
microsoft:mainfrom
abdelhadi703:fix/envfile-interpolation

Conversation

@abdelhadi703

@abdelhadi703 Abdelhadi Salmaoui (abdelhadi703) commented Oct 10, 2026 •

Copy link
Copy Markdown

.env files are supposed to support variable interpolation, but when such a file is
loaded through envFile, the values keep their ${...} references and the variable is
never substituted.

With a file like

USER=admin
EMAIL=${USER}@example.org

EMAIL came out as the literal ${USER}@example.org. Later, when the debug
configuration gets expanded, ${USER} is looked up as a launch.json variable, isn't
found, and prints "Invalid variable reference ${USER} in string". The env var that was
meant to land in the debuggee doesn't.

I made ParsedEnvironmentFile resolve ${VAR} while it parses each line, against the
variables already read from the file plus the process environment. A reference to a name we
don't know is left untouched, so ${workspaceFolder} and the other launch.json variables
keep working exactly as before.

Only a reference the value actually asked to expand is expanded. A single-quoted value and a
\${VAR} reference are literal in dotenv, and both were literal here before this change, so
they now stay as written -- the backslash itself is dropped, leaving the text ${VAR}.

Six tests were added to the existing ParsedEnvironmentFile suite: a reference to a variable
defined earlier in the same file, a reference coming from the initial environment, an unknown
reference staying literal, a reference resolved from the process environment, and the two
literal forms above.

Testing: ran the ParsedEnvironmentFile suite against the parser alone, compiled with the
repo's TypeScript and a local vscode-nls stand-in (mocha 10 does not start on Node 26, so
the compiled file runs under a short TDD shim). 13 passing with this change.

Against the first commit of this branch the two literal tests fail -- admin@example.org
where ${USER}@example.org is expected for the single-quoted one, and \admin@example.org
for the escaped one -- while the other 11 pass either way.

Fixes #13781.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Interpolation incorrectly expands quoted or escaped literals, and the tests are incomplete and environment-dependent.

2 open findings
What changed in this PR

Adds dotenv-style ${VAR} interpolation for debugger envFile parsing.

Changes:

  • Resolves references from prior entries and the process environment.
  • Preserves unknown references.
  • Adds tests and changelog documentation.
File Description
Extension/​src/​Debugger/​ParsedEnvironmentFile.ts Implements interpolation.
Extension/​test/​scenarios/​SingleRootProject/​tests/​ParsedEnvironmentFile.test.ts Adds interpolation tests.
Extension/​CHANGELOG.md Documents the fix.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Extension/src/Debugger/ParsedEnvironmentFile.ts Outdated
The quotes come off before the references are resolved, so VALUE='${SECRET}' was expanded and
a \${SECRET} reference kept its backslash while losing its meaning. Both are literal in dotenv,
and both were literal before the interpolation existed.

Adds the missing test for the process-environment fallback the changelog advertises.
@abdelhadi703

Abdelhadi Salmaoui (abdelhadi703) commented Oct 13, 2026 •

Copy link
Copy Markdown
Author

Both addressed in 66ea67b.

The quotes came off before the references were resolved, so you are right on both counts: VALUE='${SECRET}' was expanded, and \${SECRET} lost its meaning while keeping its backslash. Both are literal in dotenv, and both were literal here before the interpolation existed, so they stay as written now -- the backslash is dropped, leaving the text ${VAR}. Two tests cover the two forms.

The process-environment fallback has a test now as well: it sets a name of its own, parses a reference to it, and restores the previous value in a finally.

13 passing against the first commit of this branch. The two literal tests fail there -- admin@example.org where ${USER}@example.org is expected for the single-quoted one, and \admin@example.org for the escaped one -- and the other 11 pass either way.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Literal references are exposed to subsequent launch-variable expansion instead of reaching the debuggee unchanged.

2 open findings
2 resolved since last review

🧠 Review effort: Balanced

Comment on lines +71 to +74
if (!singleQuoted) {
value = value.replace(/(\\)?\$\{([\w\.\-]+)\}/g, (full: string, escape: string | undefined, name: string): string => {
if (escape) {
return full.substring(1);
Comment thread Extension/CHANGELOG.md
* Reduce CPU usage when resolving headers in large Windows workspaces.

### Bug Fixes
* Fix `${VAR}` interpolation in `.env` files used by `envFile`: references to variables defined earlier in the same file (or in the OS environment) are now resolved. Single-quoted values and `\${VAR}` are left as written. [#13781](https://github.com/microsoft/vscode-cpptools/issues/13781)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Pull Request

Development

Successfully merging this pull request may close these issues.

[bug] launch.json envFile not capable of variable interpolation

2 participants