tracel-ai/burn · error

deserialize_i8 is not implemented

Error message

deserialize_i8 is not implemented

What it means

The nested-value deserializer in burn-store does not implement serde's `deserialize_i8`; it panics with unimplemented!(). NestedValue-backed record deserialization only supports wider integer widths, so any struct field of type i8 (or an i8-typed enum payload) reached during record deserialization hits this panic.

Source

Thrown at crates/burn-store/src/nested/de.rs:167

                "Expected map value but got {:?}",
                self.value
            ))),
        }
    }

    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        let val = self.extract_scalar("bool", |v| v.clone().as_bool())?;
        visitor.visit_bool(val)
    }

    fn deserialize_i8<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        unimplemented!("deserialize_i8 is not implemented")
    }

    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        let val = self.extract_scalar("i16", |v| v.clone().as_i16())?;
        visitor.visit_i16(val)
    }

    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        let val = self.extract_scalar("i32", |v| v.clone().as_i32())?;
        visitor.visit_i32(val)
    }

View on GitHub (pinned to d16f7ba2ed)

Solutions

  1. Change the field type in the record item from i8 to i16/i32/i64 and convert after deserialization.
  2. Wrap the i8 field with #[serde(deserialize_with)] using a function that reads an i16 and casts to i8.
  3. Regenerate/re-save the checkpoint so its record structure matches types the nested deserializer supports.
  4. Verify Burn version match between the code that saved the record and the code loading it.

Example fix

// before
#[derive(Deserialize)]
struct Item { flag: i8 } // -> deserialize_i8 panic
// after
#[derive(Deserialize)]
struct Item { flag: i16 }
impl Item { fn flag(&self) -> i8 { self.flag as i8 } }
Defensive patterns

Strategy: validation

Validate before calling

// Reject i8 fields in record item structs before loading
fn assert_no_i8_fields<T>(_t: &T) { /* audit: grep record types for ": i8" */ }
// or deserialize_with:
fn i8_from_i16<'de, D: serde::Deserializer<'de>>(d: D) -> Result<i8, D::Error> {
    Ok(i8::try_from(i16::deserialize(d)).map_err(serde::de::Error::custom)?)
}

Try / catch

let record = std::panic::catch_unwind(|| from_nested_value::<MyItem>(value.clone()))
    .unwrap_or_else(|_| panic!("record contains i8: not supported by nested deserializer"));

Prevention

When it happens

Trigger: Loading a Burn module/record whose deserialized type contains an `i8` field, causing serde to call Deserializer::deserialize_i8 during `from_nested_value` / recorder load.

Common situations: A record item struct changed between Burn versions to include an i8 field; user-defined record items or custom adapters storing byte-sized values; loading checkpoints saved by newer Burn versions into older code (or vice versa) where field types differ.

Related errors


AI-assisted analysis of tracel-ai/burn@d16f7ba2ed (2026-09-05). Data as JSON: /api/errors/87ca42410c7686f2. Report an issue: GitHub.