Skip to content

fix: name the .env path in set_key and unset_key write errors - #711

Merged
theskumar merged 3 commits into
theskumar:mainfrom
jamalkamaladdin:fix/710-missing-dir-error
Oct 1, 2026
Merged

theskumar merged 3 commits into
theskumar:mainfrom
jamalkamaladdin:fix/710-missing-dir-error

Conversation

@jamalkamaladdin

Copy link
Copy Markdown
Contributor

Fixes #710.

src/dotenv/main.py: an OSError from creating a temporary file in rewrite() now names the .env path.
src/dotenv/cli.py: set and unset print "Error writing env file" and exit with code 2 on an OSError.
tests/test_main.py: 2 cases for set_key with a missing directory and with a read-only directory.
tests/test_cli.py: 2 cases for dotenv set with a missing directory and dotenv unset with a read-only directory.

jamalkamaladdin and others added 3 commits September 30, 2026 08:24
rewrite() creates its temporary file in the target's directory before it
touches the target. When that directory is missing or not writable, the
OSError named the temporary file instead of the .env path. The error now
carries the .env path.

`dotenv set` and `dotenv unset` print a short error and exit with code 2
on an OSError, as `get` and `list` do through stream_file.

Fixes theskumar#710.
@theskumar

Copy link
Copy Markdown
Owner

Thanks @jamalkamaladdin, this is the right fix. I reproduced #710 on main, including the read-only-directory case for both set_key and unset_key. Setting err.filename and re-raising keeps the original exception type and traceback, and closing source avoids leaking the already-open file.

Why the Windows jobs failed: the code is correct there. The test was the problem: OSError.__str__ quotes the filename with repr(), which doubles every backslash on Windows, so str(dotenv_path) in result.output can never match. The failing log shows the output was otherwise exactly right.

I pushed two commits on top of yours, without changing your commits:

  • Merge main: brings in 1.2.4 and Fix a package build deprecation warning #648, so the CHANGELOG entry lands in the right section.
  • Follow-up:
    • The two CLI tests now compare the exact (exit_code, output), building the path with {str(dotenv_path)!r}. That works on every OS and matches the literal-value style of the other CLI tests.
    • Adds a CHANGELOG entry crediting you.

I checked the tests by breaking the code on purpose. Every new test catches at least one of these bugs:

Bug introduced on purpose Caught by
Error still names the temp file all 4 new tests
set stops catching OSError test_set_missing_directory
unset stops catching OSError test_unset_read_only_directory
Exit code 1 instead of 2 both CLI tests

Locally: 264 passed, ruff check/ruff format --check clean, mypy clean on 3.10 and 3.14. I'm waiting for Windows CI on the new head.

@theskumar
theskumar merged commit 6a49d6d into theskumar:main Oct 1, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

set_key / dotenv set report a temp file path when the target's directory doesn't exist

2 participants