diesel-rs/diesel · error

expected `treat_none_as_null` field to be of type `Option

Error message

expected `treat_none_as_null` field to be of type `Option<_>`

What it means

When a field has `treat_none_as_null`, the derive must be able to represent `None` in that field's Rust type, which requires `Option<T>`. If the field's type is not an `Option<_>`, the derive fails at the field's type span, since there is no `None` value to translate to NULL.

Solutions

  1. Change the field type to `Option<T>`
  2. Remove the `treat_none_as_null` attribute if the field cannot be null
  3. Use `treat_none_as_default_value` semantics or handle defaults instead if appropriate

Example fix

// before
#[diesel(treat_none_as_null)]
nickname: String,
// after
#[diesel(treat_none_as_null)]
nickname: Option<String>,
Defensive patterns

Strategy: type-guard

Type guard

fn is_option_ty(t: &syn::Type) -> bool {
    if let syn::Type::Path(p) = t {
        p.path.segments.last().map_or(false, |s| s.ident == "Option")
    } else { false }
}

Prevention

When it happens

Trigger: Annotating a non-Option field with `#[diesel(treat_none_as_null)]`, e.g. `#[diesel(treat_none_as_null)] name: String`.

Common situations: Adding the attribute to make NULL handling explicit but forgetting to wrap the field type in `Option`; refactoring a field from `Option<T>` to `T` while keeping the attribute.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of diesel-rs/diesel@6fa6ed01b2 (2026-09-07). Data as JSON: /api/errors/f85a7ddfd3825d1c. Report an issue: GitHub.

Appendix: source

Thrown at diesel_derives/src/as_changeset.rs:72

    let mut borrowed_field_ty_bounds_guard = HashMap::new();

    for field in fields_for_update {
        // skip this field while generating the update
        if field.skip_update() {
            continue;
        }
        // Use field-level attr. with fallback to the struct-level one.
        let treat_none_as_null = match &field.treat_none_as_null {
            Some(attr) => {
                if let Some(embed) = &field.embed {
                    return Err(syn::Error::new(
                        embed.attribute_span,
                        "`embed` and `treat_none_as_default_value` are mutually exclusive",
                    ));
                }

                if !is_option_ty(&field.ty) {
                    return Err(syn::Error::new(
                        field.ty.span(),
                        "expected `treat_none_as_null` field to be of type `Option<_>`",
                    ));
                }

                attr.item
            }
            None => treat_none_as_null,
        };

        match (field.serialize_as.as_ref(), field.embed()) {
            (Some(AttributeSpanWrapper { item: ty, .. }), false) => {
                direct_field_ty.push(field_changeset_ty_serialize_as(
                    field,
                    table_name,
                    ty,
                    treat_none_as_null,
                )?);

View on GitHub (pinned to 6fa6ed01b2)