Skip to content

Fix env.dict leaking ValueError on malformed input - #492

Open
dualfroz wants to merge 1 commit into
sloria:mainfrom
dualfroz:dualfroz/dict-malformed-validation-error
Open

dualfroz wants to merge 1 commit into
sloria:mainfrom
dualfroz:dualfroz/dict-malformed-validation-error

Conversation

@dualfroz

@dualfroz dualfroz commented Sep 5, 2026

Copy link
Copy Markdown

Problem

env.dict raises an uncaught ValueError (rather than the library's
EnvValidationError) whenever an entry is missing the key-value delimiter.
This includes very common inputs such as a trailing delimiter:

import os
from environs import Env

env = Env()
os.environ["DICT"] = "key1=1,key2=2,"   # trailing comma
env.dict("DICT")
# ValueError: not enough values to unpack (expected 2, got 1)

The same happens for an empty entry ("a=1,,b=2") and for a bare key with no
delimiter ("foo"). Because a ValueError is not an EnvValidationError, it
bypasses the library's error handling entirely: it is not converted into an
EnvValidationError when eager=True, and it is not accumulated into
env.seal()'s error report when eager=False (it propagates out of the loop
instead).

Root cause

src/environs/__init__.py, _preprocess_dict built the result with a dict
comprehension:

for key, val in (
    item.split(key_value_delimiter, 1) for item in value.split(delimiter) if value
)

str.split(key_value_delimiter, 1) returns a single-element list when the
delimiter is absent, so unpacking it into key, val raises ValueError.
Everything in _field2method that turns parse failures into EnvValidationError
only catches marshmallow.ValidationError, so the ValueError escapes.

Fix

Iterate explicitly and raise marshmallow.ValidationError for any entry that
does not contain the key-value delimiter. This is the same exception type the
rest of the parsing path already raises, so it is correctly converted into
EnvValidationError (eager) or collected into the deferred error report
(non-eager), and the message names the offending entry.

Test

tests/test_environs.py::TestCasting::test_invalid_dict_raises_validation_error
parametrizes a trailing delimiter, an empty entry, and a bare key, asserting
each raises environs.EnvValidationError. All three fail on the current main
(ValueError: not enough values to unpack) and pass with the fix. The existing
env.dict tests continue to pass.

_preprocess_dict split each entry with str.split(key_value_delimiter, 1)
and unpacked the result into key, val. When an entry lacked the delimiter
(a trailing delimiter, an empty entry, or a bare key) the split returned
a single-element list and the unpacking raised a bare ValueError, which
is not an EnvValidationError and so bypassed the library's error handling
entirely.

Iterate explicitly and raise marshmallow.ValidationError for any entry
missing the key-value delimiter, so malformed input is reported through
the normal EnvValidationError path.

This branch has not been deployed

No deployments
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.

1 participant