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
- Add a `default` callback to the res.format object so unmatched Accept headers get a sensible fallback instead of a 406.
- Handle the 406 in your error middleware (the error has status 406 and a `types` array) to return a clean message.
- 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
- Always provide a default handler in res.format — without it, an unmatched Accept header raises this 406 error
- Handle the 406 in your error middleware so clients get a clean response instead of a stack trace
- Test routes with Accept: application/xml or other types you don't serve
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
- name argument is required to req.get
- name must be a string to req.get
- app.use() requires a middleware function
- callback function required
- Failed to lookup view "' + name + '" in views ' + dirs
AI-assisted analysis of expressjs/express@a3714473fe (2026-07-31).
Data as JSON: /data/errors/32077ce51e2c037d.json.
Report an issue: GitHub ↗.