JSON Errors and Exception Mode

By default, failures return null or false. json_last_error_msg() holds the reason only until the next JSON call:

An ambiguous null, an overwritten error, and exception modePHP
<?php
echo json_encode([json_decode('{"qty":2,}'), json_decode('null'), json_last_error_msg()]), "\n";
try {
  json_encode("caf\xE9", JSON_THROW_ON_ERROR);                  // a Latin-1 byte
} catch (JsonException $e) {
  printf("%s %d: %s\n", $e::class, $e->getCode(), $e->getMessage());
}
Output
[null,null,"No error"]
JsonException 5: Malformed UTF-8 characters, possibly incorrectly encoded

The syntax error and the valid null look identical, and the second call erased the error. JSON_THROW_ON_ERROR (PHP 7.3) throws with a JSON_ERROR_* code (5 is JSON_ERROR_UTF8) and leaves the global error state alone. Use it everywhere, and map the exception to a 400. Convert legacy text to UTF-8 first (Multibyte Strings).