expressjs/express · warning · HttpError

Not Acceptable

Error message

Not Acceptable

What it means

Not thrown directly — `res.format(obj)` calls `next(createError(406, {...}))` when the request's Accept header matches none of the content types offered in the object and no `default` handler is provided. "Not Acceptable" is the standard HTTP 406 reason phrase; the error carries a `types` property listing the types the server could have produced.

Source

Thrown at lib/response.js:590

  var req = this.req;
  var next = req.next;

  var keys = Object.keys(obj)
    .filter(function (v) { return v !== 'default' })

  var key = keys.length > 0
    ? req.accepts(keys)
    : false;

  this.vary("Accept");

  if (key) {
    this.set('Content-Type', normalizeType(key).value);
    obj[key](req, this, next);
  } else if (obj.default) {
    obj.default(req, this, next)
  } else {
    next(createError(406, {
      types: normalizeTypes(keys).map(function (o) { return o.value })
    }))
  }

  return this;
};

/**
 * Set _Content-Disposition_ header to _attachment_ with optional `filename`.
 *
 * @param {String} filename
 * @return {ServerResponse}
 * @public
 */

res.attachment = function attachment(filename) {
  const name = filename !== undefined ? basename(filename) : undefined;
  if (name) {

View on GitHub ↗ (pinned to a3714473fe)

Solutions

  1. Add a `default` callback to the res.format object so unmatched Accept headers get a sensible fallback instead of a 406.
  2. Handle the 406 in your error middleware (the error has status 406 and a `types` array) to return a clean message.
  3. If negotiation isn't really needed, drop res.format and always send one type.

Example fix

// before
res.format({ json: () => res.json(data), html: () => res.render('page', data) });
// after
res.format({ json: () => res.json(data), html: () => res.render('page', data), default: () => res.status(406).json({ error: 'Not Acceptable', accepts: ['application/json', 'text/html'] }) });
Defensive patterns

Strategy: fallback

Validate before calling

res.format({
  json() { res.json(payload); },
  html() { res.render('view', payload); },
  default() { res.status(406).send('Not Acceptable'); }
});

Try / catch

app.use((err, req, res, next) => {
  if (err.status === 406) {
    return res.status(406).json({ error: 'no acceptable representation' });
  }
  next(err);
});

Prevention

When it happens

Trigger: A client sends `Accept: application/xml` (or any type not among the keys passed to `res.format`) to a route using `res.format({ json: ..., html: ... })` with no `default` key; API clients sending overly strict Accept headers.

Common situations: Strict API clients or security scanners sending unusual Accept headers; curl/tests specifying an unsupported Accept value; content-negotiation routes that forgot a default branch, turning benign clients into 406 responses.

Related errors


AI-assisted analysis of expressjs/express@a3714473fe (2026-07-31). Data as JSON: /data/errors/32077ce51e2c037d.json. Report an issue: GitHub ↗.