balderdashy/sails · warning

Cannot change primary key via update blueprint; ignoring val

Error message

Cannot change primary key via update blueprint; ignoring value sent for `${Model.primaryKey}`

What it means

In the update blueprint's parameter parsing (parse-blueprint-options.js), if the request params include a new value for the model's primary key that differs from the PK in the request's where criteria, Sails logs this warning and forcibly keeps the original PK. Changing a record's primary key via the update blueprint is not allowed — you must drop and re-create the record.

Source

Thrown at lib/hooks/blueprints/parse-blueprint-options.js:316

      // Note that we do NOT set `fetch: true`, because if we do so, some versions
      // of Waterline complain that `fetch` need not be included with .updateOne().
      // (Now that we take advantage of .updateOne() in blueprints, this is a thing.)
      queryOptions.meta = {};

      //  ┌─┐┌─┐┬─┐┌─┐┌─┐  ┬  ┬┌─┐┬  ┬ ┬┌─┐┌─┐
      //  ├─┘├─┤├┬┘└─┐├┤   └┐┌┘├─┤│  │ │├┤ └─┐
      //  ┴  ┴ ┴┴└─└─┘└─┘   └┘ ┴ ┴┴─┘└─┘└─┘└─┘

      queryOptions.valuesToSet = (function getValuesToSet(){

        // Use all of the request params as values for the new record, _except_ `id`.
        var values = _.omit(req.allParams(), 'id');

        // No matter what, don't allow changing the PK via the update blueprint
        // (you should just drop and re-add the record if that's what you really want)
        if (typeof values[Model.primaryKey] !== 'undefined' && values[Model.primaryKey] !== queryOptions.criteria.where[Model.primaryKey]) {
          req._sails.log.warn('Cannot change primary key via update blueprint; ignoring value sent for `' + Model.primaryKey + '`');
        }
        // Make sure the primary key is unchanged
        values[Model.primaryKey] = queryOptions.criteria.where[Model.primaryKey];

        return values;

      })();


      break;

    //  ██████╗ ███████╗███████╗████████╗██████╗  ██████╗ ██╗   ██╗
    //  ██╔══██╗██╔════╝██╔════╝╚══██╔══╝██╔══██╗██╔═══██╗╚██╗ ██╔╝
    //  ██║  ██║█████╗  ███████╗   ██║   ██████╔╝██║   ██║ ╚████╔╝
    //  ██║  ██║██╔══╝  ╚════██║   ██║   ██╔══██╗██║   ██║  ╚██╔╝
    //  ██████╔╝███████╗███████║   ██║   ██║  ██║╚██████╔╝   ██║
    //  ╚═════╝ ╚══════╝╚══════╝   ╚═╝   ╚═╝  ╚═╝ ╚═════╝    ╚═╝
    case 'destroy':

View on GitHub (pinned to 7b76422cc2)

Solutions

  1. Remove the primary key field from the update payload; send only mutable attributes.
  2. Ensure the id in the URL/body matches the record you intend to update.
  3. If PK changes are truly required, drop and re-add the record (custom action or direct database operation).

Example fix

// before
await fetch('/api/users/7', {method:'PUT', body: JSON.stringify({id: 9, name: 'x'})});
// after
await fetch('/api/users/7', {method:'PUT', body: JSON.stringify({name: 'x'})});
Defensive patterns

Strategy: validation

Validate before calling

const params = req.allParams();
if (params.id !== undefined && req.param('id') !== undefined && String(params[Model.primaryKey]) !== String(req.options.values?.where?.[Model.primaryKey] ?? req.params[Model.primaryKey])) {
  delete params[Model.primaryKey]; // strip PK before sending update
}

Prevention

When it happens

Trigger: PUT/PATCH to a blueprint update route with a body that includes a field named after the primary key (usually 'id') whose value differs from the record's actual id in queryOptions.criteria.where.

Common situations: Client forms send the whole record including the id field while the route embeds a different id, generic UI components that serialize all fields on update, or bulk-edit frontends that let users edit the id column.


AI-assisted analysis of balderdashy/sails@7b76422cc2 (2026-09-01). Data as JSON: /api/errors/c8855ad17a6780c4. Report an issue: GitHub.