Summary
Since 4.0.0 (still in 5.0.0), a key that is present in .env with an empty value (API_KEY=) is treated as if it were missing. With safe: true and allowUndefined: false the build fails:
"API_KEY" is not defined in .env
In 3.4.11 the same file worked: the import compiled to "".
Cause
index.js (5.0.0, lines 127–129) builds the final env object with undefObjectAssign({}, fileEnv):
env = (options.safe)
? safeObjectAssign(undefObjectAssign({}, fileEnv), dotenvTemporary, modeExceptions)
: undefObjectAssign(undefObjectAssign({}, fileEnv), dotenvTemporary)
undefObjectAssign copies a key only when its value is truthy, so every empty-string value is dropped before the Object.hasOwn(env, importedId) check runs. dotenv.parse itself does return the key with "".
In 3.4.11 the overlays (.env.<mode>, .env.local, …) were merged into the parsed main-file object, so a key present in the main .env with an empty value stayed defined:
env = (options.safe) ? safeObjectAssign(undefObjectAssign(undefObjectAssign(undefObjectAssign(parsed, modeParsed), localParsed), modeLocalParsed), dotenvTemporary, [...])
The extra undefObjectAssign({}, fileEnv) step is what changed the behaviour.
Reproduction
.env:
Babel config:
['module:react-native-dotenv', { moduleName: '@env', safe: true, allowUndefined: false }]
Source:
import { API_KEY } from '@env';
export const a = API_KEY;
- 3.4.11 →
export const a = "";
- 5.0.0 →
"API_KEY" is not defined in .env
Expected
A key that exists in the file, even with an empty value, is defined (as the empty string). Leaving an optional secret blank in local development while CI fills it in is a common pattern, and the safe mode error message is misleading here because the key is defined.
Suggested fix
Keep the fresh object but do not re-filter it:
env = (options.safe)
? safeObjectAssign(Object.assign({}, fileEnv), dotenvTemporary, modeExceptions)
: Object.assign(Object.assign({}, fileEnv), dotenvTemporary)
safeObjectAssign still refuses to overwrite with a falsy host value, and dotenvTemporary is already filtered, so nothing else changes. Happy to open a PR if this is the preferred direction.
Versions: react-native-dotenv 5.0.0, dotenv 18.0.3, @babel/core 7.28.6, Node 24.
Summary
Since 4.0.0 (still in 5.0.0), a key that is present in
.envwith an empty value (API_KEY=) is treated as if it were missing. Withsafe: trueandallowUndefined: falsethe build fails:In 3.4.11 the same file worked: the import compiled to
"".Cause
index.js(5.0.0, lines 127–129) builds the finalenvobject withundefObjectAssign({}, fileEnv):undefObjectAssigncopies a key only when its value is truthy, so every empty-string value is dropped before theObject.hasOwn(env, importedId)check runs.dotenv.parseitself does return the key with"".In 3.4.11 the overlays (
.env.<mode>,.env.local, …) were merged into the parsed main-file object, so a key present in the main.envwith an empty value stayed defined:The extra
undefObjectAssign({}, fileEnv)step is what changed the behaviour.Reproduction
.env:Babel config:
Source:
export const a = "";"API_KEY" is not defined in .envExpected
A key that exists in the file, even with an empty value, is defined (as the empty string). Leaving an optional secret blank in local development while CI fills it in is a common pattern, and the
safemode error message is misleading here because the key is defined.Suggested fix
Keep the fresh object but do not re-filter it:
safeObjectAssignstill refuses to overwrite with a falsy host value, anddotenvTemporaryis already filtered, so nothing else changes. Happy to open a PR if this is the preferred direction.Versions: react-native-dotenv 5.0.0, dotenv 18.0.3, @babel/core 7.28.6, Node 24.