diesel-rs/diesel · error

deriving `AsChangeset` on a structure that only contains…

Error message

deriving `AsChangeset` on a structure that only contains primary keys isn't supported.
help: if you want to change the primary key of a row, you should do so with `.set(table::id.eq(new_id))`.
note: `#[derive(AsChangeset)]` never changes the primary key of a row.

What it means

`#[derive(AsChangeset)]` builds the set of columns to update from the struct's non-primary-key fields. If after removing primary-key fields nothing remains, the derive cannot generate a meaningful changeset and refuses to compile, explaining that primary keys are never changed by this derive.

Solutions

  1. Add at least one non-primary-key field to the struct
  2. Remove the `AsChangeset` derive and use `.set(table::id.eq(new_id))` directly to change the primary key
  3. Use `dsl::UpdateTarget`/`update(...).set(...)` manually instead of the derive

Example fix

// before
#[derive(AsChangeset)]
struct UserPk { id: i32 }
// after — change PK directly
 diesel::update(users.find(id)).set(users.id.eq(new_id))
Defensive patterns

Strategy: validation

Validate before calling

// Before deriving, ensure at least one field is not a primary key
let non_pk = fields.iter().filter(|f| !is_primary_key(f)).count();
assert!(non_pk > 0, "AsChangeset requires at least one non-PK field");

Prevention

When it happens

Trigger: Deriving `AsChangeset` on a struct where every field is annotated as (or resolves to) the table's primary key, so `fields_for_update` is empty.

Common situations: A struct representing just a row's ID(s) (e.g. `struct UserId { id: i32 }`); structs whose only column is the PK after renames via `#[column_name]`.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at diesel_derives/src/as_changeset.rs:31

pub fn derive(item: DeriveInput) -> Result<TokenStream> {
    let model = Model::from_item(&item, false, false)?;

    let struct_name = &item.ident;
    let table_name = &model.table_names()[0];

    let fields_for_update = model
        .fields()
        .iter()
        .filter(|f| {
            !model
                .primary_key_names
                .iter()
                .any(|p| f.column_name().map(|f| f == *p).unwrap_or_default())
        })
        .collect::<Vec<_>>();

    if fields_for_update.is_empty() {
        return Err(syn::Error::new(
            proc_macro2::Span::mixed_site(),
            "deriving `AsChangeset` on a structure that only contains primary keys isn't supported.\n\
             help: if you want to change the primary key of a row, you should do so with `.set(table::id.eq(new_id))`.\n\
             note: `#[derive(AsChangeset)]` never changes the primary key of a row.",
        ));
    }

    let treat_none_as_null = model.treat_none_as_null();

    let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();

    let mut generate_borrowed_changeset = true;

    let mut direct_field_ty = Vec::with_capacity(fields_for_update.len());
    let mut direct_field_assign = Vec::with_capacity(fields_for_update.len());
    let mut ref_field_ty = Vec::with_capacity(fields_for_update.len());
    let mut ref_field_assign = Vec::with_capacity(fields_for_update.len());

View on GitHub (pinned to 6fa6ed01b2)