Skip to content

Use 'Invalid value' wording for Literal validation errors#1010

Open
Siyet wants to merge 2 commits into859-literal-boolfrom
1009-literal-error-messages
Open

Use 'Invalid value' wording for Literal validation errors#1010
Siyet wants to merge 2 commits into859-literal-boolfrom
1009-literal-error-messages

Conversation

@Siyet
Copy link
Copy Markdown
Collaborator

@Siyet Siyet commented Apr 10, 2026

Closes #1009.

Validation errors for typing.Literal types previously used the wording Invalid enum value <val>, which is misleading because the user is not using enum.Enum.

This change differentiates Enum and Literal in _Lookup_OnMissing: when lookup->cls != NULL (Enum) the message stays Invalid enum value <val>, otherwise (Literal) it becomes Invalid value <val>. The bool literal decode paths added in #1004 are updated to the same wording for consistency.

Examples

Before:

Literal[1, 2, 3]    + 4      -> Invalid enum value 4
Literal["a", "b"]   + "c"    -> Invalid enum value 'c'
Literal[True]       + false  -> Invalid enum value False

After:

Literal[1, 2, 3]    + 4      -> Invalid value 4
Literal["a", "b"]   + "c"    -> Invalid value 'c'
Literal[True]       + false  -> Invalid value False

Enum messages are unchanged:

Color.RED | Color.BLUE + "green" -> Invalid enum value 'green'

Notes

@Siyet
Copy link
Copy Markdown
Collaborator Author

Siyet commented Apr 10, 2026

The failing build job here is unrelated to this PR: it's the link checker tripping on the Pydantic docs redirect (docs.pydantic.dev/latest/pydantic.dev/docs/validation/...), which is fixed in #1008. Once #1008 lands and this branch is rebased, CI should go green.

@jcrist
Copy link
Copy Markdown
Owner

jcrist commented Apr 10, 2026

The implementation seems fine, so happy to merge. However, I'm not 100% sure this is the right move.

msgspec throws the same errors everywhere, regardless of decoding tool (msgspec.json.decode, msgspec.msgpack.decode, msgspec.convert, ...). To make the validation errors as universal as possible, I decided early on to use syntax that would make sense to a consumer of a JSON API (e.g. refer to JSON types and schema requirements) over things that more reveal implementation details (e.g. python class names). This is of course tricky to do universally, but was a general guideline.

JSON schema refers to collections of possible values as "enums", hence the use of "enum" in the error here. In Python of course these can be represented as a enum.Enum or as a Literal, but to a JSON API consumer that's not relevant information.

I don't think dropping the "enum" in the error messages makes things more confusing (and still doesn't leak python implementation details), so if you think what you have here is clearer than I'm happy with it. Just sharing context for why things were the way they were before. Up to you.

@Siyet
Copy link
Copy Markdown
Collaborator Author

Siyet commented Apr 11, 2026

Thanks for the context. The push toward "Invalid value" came down to one thing: enum is overloaded. JSON Schema readers parse it as the schema keyword (your intent), but most msgspec users read it as enum.Enum and get confused when their Literal[...] field reports Invalid enum value. That's how #1009 was filed.

It also gets ambiguous when both coexist: a struct with a Color (Enum) field and a Literal["a", "b"] field today produces identical wording for both. Splitting them ("enum value" for real Enum, "value" for Literal) disambiguates without leaking Python details, since Literal never appears in the message. Real Enum errors are unchanged, so JSON Schema consumers aren't affected.

@provinzkraut
Copy link
Copy Markdown
Contributor

It also gets ambiguous when both coexist: a struct with a Color (Enum) field and a Literal["a", "b"] field today produces identical wording for both. Splitting them ("enum value" for real Enum, "value" for Literal) disambiguates without leaking Python details, since Literal never appears in the message. Real Enum errors are unchanged, so JSON Schema consumers aren't affected.

This would introduce a different ambiguity though: If it refers to an enum.Enum, it's explicitly named in the error, if it's for a Literal, it's vague. This does feel like an arbitrary distinction, so IMO either the target type shouldn't be mentioned in any case, or it should be mentioned in every case.

@Siyet
Copy link
Copy Markdown
Collaborator Author

Siyet commented Apr 12, 2026

Fair point, the split is arbitrary. I went through the alternatives:

Mention the type in every case. The only candidate term for Literal is "literal", but that's Python-specific (typing.Literal). JSON Schema has no separate keyword for it: both Enum and Literal map to the enum keyword. Using "Invalid literal value" would leak implementation details, which goes against the error message design. No existing message mentions Python type names (no "dataclass", "TypedDict", "NamedTuple", etc.).

Qualify as "JSON Schema enum". Doesn't work either: msgspec errors are protocol-agnostic (same message for JSON, MsgPack, convert). Referencing "JSON Schema" in a MsgPack decode error would be more confusing, not less.

Include the field name in the message body. Already covered by the path suffix (- at $.field), so redundant.

That leaves dropping "enum" from both, using "Invalid value" universally. This is consistent (no type qualifier in either case), doesn't leak implementation details, and matches the existing union tag errors which already use plain "Invalid value".

Bigger change than the current PR since it touches Enum errors too, but it's the only option that resolves the inconsistency cleanly. I can update the branch if you and @jcrist are fine with this direction.

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.

3 participants