{"record":{"id":"c0288906ab0fcdef","repo":"overleaf/overleaf","slug":"unprocessable-entity","errorCode":null,"errorMessage":"unprocessable entity","messagePattern":"unprocessable entity","errorType":"http","errorClass":null,"httpStatus":422,"severity":"error","filePath":"services/web/app/src/Features/Errors/HttpErrorHandler.mjs","lineNumber":121,"sourceCode":"      default:\n        return plainTextResponse(res, 'restricted')\n    }\n  },\n\n  notFound(req, res, message = 'not found', info = {}) {\n    res.status(404)\n    switch (req.accepts(['html', 'json'])) {\n      case 'html':\n        return res.render('general/404', { title: 'page_not_found' })\n      case 'json':\n        return renderJSONError(res, message, info)\n      default:\n        return plainTextResponse(res, 'not found')\n    }\n  },\n\n  unprocessableEntity(req, res, message = 'unprocessable entity', info = {}) {\n    res.status(422)\n    switch (req.accepts(['html', 'json'])) {\n      case 'html':\n        return res.render('general/400', {\n          title: 'Client Error',\n          message,\n        })\n      case 'json':\n        return renderJSONError(res, message, info)\n      default:\n        return plainTextResponse(res, 'unprocessable entity')\n    }\n  },\n\n  legacyInternal(req, res, message, err) {\n    req.logger.addFields({ err })\n    req.logger.setLevel('error')\n    handleGeneric500Error(req, res, 500, message)\n  },","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/overleaf/overleaf/blob/28ad3b03b71cb4311decdcb55c36b33ec10d72db/services/web/app/src/Features/Errors/HttpErrorHandler.mjs#L103-L139","documentation":"HttpErrorHandler.unprocessableEntity sends HTTP 422 with message defaulting to 'unprocessable entity'. HTML clients get the general/400 'Client Error' page with the message; JSON clients get message and info via renderJSONError. It signals the request is syntactically valid but semantically cannot be processed.","triggerScenarios":"Calling HttpErrorHandler.unprocessableEntity(req, res, message, info) when a request passes parsing/validation but fails business rules, e.g. malformed entity references or state-machine violations.","commonSituations":"Well-formed JSON whose field values are logically invalid (bad enums, impossible dates), payloads that skip required domain checks, or API clients not updated for changed validation rules.","solutions":["Read the message/info to identify which semantic rule failed and correct the payload","Run the same domain validations client-side before submitting","Check API changelog for validation rules that changed between versions"],"exampleFix":"// before\nawait updateDoc(docId, { version: -1 }) // 422\n// after\nif (version > 0) await updateDoc(docId, { version })","handlingStrategy":"validation","validationCode":"function isValidPayload(p) {\n  return p && typeof p.version === 'number' && p.version > 0 &&\n    typeof p.name === 'string' && p.name.length > 0\n}\nif (!isValidPayload(payload)) {\n  // fix payload before submitting\n}","typeGuard":null,"tryCatchPattern":"try {\n  await submit(payload)\n} catch (err) {\n  if (err.status === 422) {\n    // inspect err.body.message/info for the failing semantic rule\n  }\n}","preventionTips":["Mirror server-side business rules in client validation","Keep API schema docs in sync with validation changes","Test payloads against staging before production calls"],"tags":["http-422","unprocessable-entity","validation"],"backgroundTag":"unprocessable-entity-422","analyzedSha":"28ad3b03b71cb4311decdcb55c36b33ec10d72db","analyzedAt":"2026-09-03T02:10:22.807Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}