Skip to content

Serde drops error source information when deserializing using try_from #3097

Description

@axoking

When using a custom type and #[serde(try_from = "..."], errors that the try_from call throws are not forwarded correctly. Instead, they are converted into strings with no control about how the conversion behaves, and the original error is dropped instead of saving it as a source. This means that the actual error that caused the deserialization to fail will be forgotten if it is wrapped in some context management.

Example code:

use anyhow::{Context, anyhow};
use serde_derive::Deserialize;

#[derive(Deserialize, Debug)]
#[serde(try_from = "u8", into = "u8")]
enum MyType {
	Alpha, Bravo, Charlie
}

impl TryFrom<u8> for MyType {
	type Error = anyhow::Error;

	fn try_from(value: u8) -> Result<Self, Self::Error> {
		number_to_my_type(value)
	}
}

fn number_to_my_type(number: u8) -> Result<MyType, anyhow::Error> {
	// this is the only error which will be printed
	number_to_my_type_inner(number).context("number to my type: Outer function")
}

fn number_to_my_type_inner(number: u8) -> Result<MyType, anyhow::Error> {
	match number {
		67 => Ok(MyType::Alpha),
		68 => Ok(MyType::Bravo),
		69 => Ok(MyType::Charlie),
		// but I want to see this important error message
		num => Err(anyhow!("IMPORTANT ERROR MESSAGE: {num} was an invalid number"))
	}
}

fn parse_json() -> Result<MyType, anyhow::Error> {
	serde_json::from_str("128")
		// at this point, a serde_json Error is returned, which is created by calling custom() I guess
		// the problem is that custom() takes a variable of type Into<String>, so the only thing serde_json can do with it
		// is to convert it to a string, but it can't save any important information about the error
		.context("Parsing a string")
}

fn main() {
	let x = parse_json().expect("error");
	println!("{x:?}");
}

This code prints

thread 'main' (119114) panicked at src/main.rs:36:26:
error: Parsing a string

Caused by:
    number to my type: Outer function

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions