Skip to content

Shorten Codable error messages so they are not redundant with debug descriptions from stdlib#1770

Draft
ZevEisenberg wants to merge 1 commit intoswiftlang:mainfrom
ZevEisenberg:main
Draft

Shorten Codable error messages so they are not redundant with debug descriptions from stdlib#1770
ZevEisenberg wants to merge 1 commit intoswiftlang:mainfrom
ZevEisenberg:main

Conversation

@ZevEisenberg
Copy link
Copy Markdown

@ZevEisenberg ZevEisenberg commented Feb 23, 2026

Shorten error messages from built-in encoders and decoders so they are not redundant with debug descriptions from EncodingError and DecodingError in the stdlib.

Motivation:

Consider the following error message:

typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [_CodingKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "address", intValue: nil), CodingKeys(stringValue: "city", intValue: nil), CodingKeys(stringValue: "birds", intValue: nil), _CodingKey(stringValue: "Index 1", intValue: 1), CodingKeys(stringValue: "name", intValue: nil)], debugDescription: "Expected to decode String but found number instead.", underlyingError: nil))

Following changes from SE-0489, it is shortened to the following:

DecodingError.typeMismatch: expected value of type String. Path: [0].address.city.birds[1].name. Debug description: Expected to decode String but found number instead.

But we can do better. Note that there is still redundant information making the error message unnecessarily long. Let's break it down into pieces:

Message component Origin
DecodingError.typeMismatch: expected value of type String. Path: [0].address.city.birds[1].name. Debug description: Swift.DecodingError.debugDescription
Expected to decode String but found number instead Foundation.JSONDecoder
. Swift.DecodingError.debugDescription

The stdlib already says what kind of value we expected (a String), but not what type was expected. Foundation provides the expected type a second time, as well as the actual found type.

Modifications:

  1. Shortened error messages generated by encoders and decoders in Foundation so as not to carry information redundant to the messages from the stdlib.
  2. Included, in the debug description, the date string that causes IS08601 decoding failures. I imagine there are cases where we might want to be careful and avoid printing the full string, though?
  3. I caught a case where PlistDecoderGeneric was seemingly doing an invalid check for is any UnkeyedDecodingContainer - that check was failing on a test case which did not fail with JSONDecoder, so I modified the check in PlistDecoderGeneric to match.

Testing:

I added unit tests for the errors generated by decoders in Foundation. I'm not testing the full debugDescription of the resulting error, since that's controlled by the stdlib, but that choice makes it hard to see the full impact of this change. In order to see the full change, capture the error thrown by the encoding/decoding failure and print String(reflecting: error). I'm wondering whether there's anything I can do to improve this setup?

Open Questions:

  1. I found a few parts of JSONDecoder and PropertyListDecoder that seem to be unreachable. Are they in fact unreachable?
  2. Is the change to cast in PlistDecoderGeneric ok?
  3. Should we merge [SE-0489] tweak valueNotFound debug description swift#87408? If so, it will slightly impact the strings we end up with in this PR. It was the initial investigation for this Foundation PR that led me to notice the discrepancy with null values in the stdlib.
  4. In general, does this approach seem good and worthwhile? I've identified almost 100 additional thrown errors in the plist decoder, but I want to get some encouragement that I'm going in the right direction before I devote more time to this.

Comment on lines 537 to 540
throw DecodingError.typeMismatch([String: Any].self, DecodingError.Context(
codingPath: self.codingPath,
debugDescription: "Expected to decode \([String: Any].self) but found \(topValue.debugDataTypeDescription) instead."
))
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this code is only reachable in FOUNDATION_FRAMEWORK builds, but I couldn't say for sure. In any case, I was not able to reach it from the public interface of JSONDecoder, and I was hesitant to start writing tests for internal functions.

@ZevEisenberg
Copy link
Copy Markdown
Author

Oh, and: would a change like this require a Foundation evolution proposal, mirroring SE-0489? It’s changing the values produced by the decoders, but only the debugDescription, and it’s not changing any conformances.

@bobgilmore
Copy link
Copy Markdown

bobgilmore commented Feb 24, 2026

I know that where you said

Foundation provides the expected type a second time, as well as the expected type.

You meant "Foundation provides the expected type a second time, as well as the actual type.", but I can't help but think that you were channeling the Department of Redundancy Department, which is, after all, the entire point of this PR!

#redundant #redundant

@ZevEisenberg
Copy link
Copy Markdown
Author

#redundant #redundant

Fixed! And rebased for good measure.

… debug descriptions in cases where DecodingError already provides the same information.
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.

2 participants