Skip to content

Use 'Invalid value' wording for Literal and Enum validation errors - #1010

Open
Siyet wants to merge 1 commit into
mainfrom
1009-literal-error-messages
Open

Use 'Invalid value' wording for Literal and Enum validation errors#1010
Siyet wants to merge 1 commit into
mainfrom
1009-literal-error-messages

Conversation

@Siyet

@Siyet Siyet commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

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 (see #1009).

Following the discussion below, this uses a single universal wording Invalid value <val> for both Literal and Enum (and the bool literals from #1004), rather than splitting the two by type. The target type is never named in the message, which resolves the "arbitrary distinction" concern and stays consistent with the existing union tag errors that already raise plain Invalid value.

Before:

Literal[1, 2, 3]   + 4       -> Invalid enum value 4
Fruit (Enum)       + "grape"  -> Invalid enum value 'grape'

After:

Literal[1, 2, 3]   + 4       -> Invalid value 4
Fruit (Enum)       + "grape"  -> Invalid value 'grape'

Tests and docs updated; full unit suite passes (6368 passed).

@Siyet

Siyet commented Apr 10, 2026

Copy link
Copy Markdown
Contributor Author

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

jcrist commented Apr 10, 2026

Copy link
Copy Markdown
Member

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

Siyet commented Apr 11, 2026

Copy link
Copy Markdown
Contributor Author

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
Member

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

Siyet commented Apr 12, 2026

Copy link
Copy Markdown
Contributor Author

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.

@Siyet
Siyet changed the base branch from 859-literal-bool to main April 20, 2026 19:01
@Siyet
Siyet force-pushed the 1009-literal-error-messages branch from 1cf786a to 847c304 Compare April 20, 2026 19:01
@Siyet
Siyet force-pushed the 1009-literal-error-messages branch from 4d088fe to 95bb505 Compare July 4, 2026 09:33
@Siyet
Siyet temporarily deployed to docs-preview July 4, 2026 09:33 — with GitHub Actions Inactive
@Siyet Siyet changed the title Use 'Invalid value' wording for Literal validation errors Use 'Invalid value' wording for Literal and Enum validation errors Jul 4, 2026
@Siyet

Siyet commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Updated the branch to use Invalid value universally for both Enum and Literal, dropping the earlier split.

@provinzkraut this addresses the arbitrary-distinction point: the target type is no longer named in either case, and it matches the existing union tag errors which already raise plain Invalid value.

@jcrist heads up that this also changes the real Enum messages (Invalid enum value -> Invalid value), so it does step on the JSON Schema "enum" wording you described above. My read is that dropping "enum" still doesn't leak Python details and isn't less clear for a JSON API consumer, while it buys consistency across Enum, Literal, and union tags. Since it touches wording you chose deliberately, wanted your explicit ok before merging.

Rebased on main, tests and docs updated, full unit suite green (6368 passed).

@codspeed-hq

codspeed-hq Bot commented Jul 4, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 139 untouched benchmarks
⏩ 135 skipped benchmarks1


Comparing 1009-literal-error-messages (72c42af) with main (9ddc0e9)

Open in CodSpeed

Footnotes

  1. 135 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@ofek

ofek commented Jul 4, 2026

Copy link
Copy Markdown
Member

Have we considered the word "choice"? I think that fits quite nicely and at the very least removes ambiguity.

@Siyet

Siyet commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

"choice" does read more precisely than "value". My hesitation is scope: to get consistent wording everywhere we'd also have to change the union tag errors (they already say Invalid value) and any other "not in the allowed set" message, so more error strings shift, and each one is something a user could be matching on literally. ZeroVer lets us land that kind of break in a minor, but churning error strings that don't need to change is a poor trade for a marginal wording gain.

Invalid value keeps the change contained and already lines enum/literal up with the existing tag errors, so I lean that way unless we deliberately want the broader rename. Not attached to it if you and @jcrist would rather standardize on "choice" everywhere.

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.

Validation errors for Literal types incorrectly say 'enum value'

4 participants