UTF-8 BOM: JSON-parsefouten en CSV-problemen oplossen
Een UTF-8 BOM die een JSON-parsefout veroorzaakt, bestaat uit drie bytes die je niet kunt zien. In je editor ziet het bestand er schoon uit, cat drukt precies af wat je verwacht, je linter klaagt niet, en toch struikelt JSON.parse over het allereerste teken.
Gemeten op node v25.8.2 ziet de fout er zo uit:
SyntaxError: Unexpected token '', "{"a":1}" is not valid JSON
Wat je terminal tussen die aanhalingstekens ook getekend heeft, het is één teken: U+FEFF, opgeslagen als de bytes EF BB BF. Strikte JSON heeft er geen plek voor. Een parser verwacht op positie 0 een {, een [, een cijfer, een aanhalingsteken of witruimte, en U+FEFF is geen van die dingen.
Weet je al zeker dat het om een BOM gaat, kies dan de kant die je zelf in de hand hebt:
| Waar je iets kunt veranderen | De oplossing |
|---|---|
| Node, bij het lezen van een bestand | JSON.parse(raw.replace(/^/, '')) |
| Python, bij het lezen van een bestand | open(path, encoding='utf-8-sig') |
| Het bestand op schijf | tail -c +4 data.json > clean.json |
De rest van deze pagina gaat over de gevallen waarin dat niet volstaat: fouten die op een BOM lijken maar het niet zijn, de bron die hem telkens terugzet, en het ene formaat waarin verwijderen juist de bug is in plaats van de oplossing. Wat een BOM precies is en of een nieuw bestand er een hoort te hebben, staat in de UTF-8 vs UTF-16 vs Unicode — complete encoding-gids. Deze pagina gaat ervan uit dat die van jou al iets kapot heeft gemaakt.
Alle metingen hieronder zijn gedaan op node v25.8.2 en Python 3.14.5.
1. Wat je foutmelding uitsluit voordat je de BOM de schuld geeft
De meeste mensen die op een JSON-fout op positie 0 zoeken, hebben helemaal geen BOM. Vier verschillende problemen leveren een melding met dezelfde vorm op, en één blik op het teken tussen de aanhalingstekens haalt ze uit elkaar. Dit zijn de letterlijke strings die V8 produceert:
| Fouttekst | Wat het werkelijk is | Volgende stap |
|---|---|---|
Unexpected token '', "{"a":1}" is not valid JSON | UTF-8 BOM op byte 0 | Sectie 2 |
Unexpected token '<', "<!DOCTYPE "... is not valid JSON | De response was HTML: een foutpagina, een redirect naar een loginscherm, een melding van een proxy | Log de ruwe body en de statuscode |
Unexpected end of JSON input | De body was leeg | Controleer de statuscode en Content-Length |
"undefined" is not valid JSON | Je gaf JSON.parse een variabele die nooit een waarde heeft gekregen | Repareer de aanroepende code |
De regel is kort genoeg om te onthouden. Lees het teken tussen de enkele aanhalingstekens. < betekent dat je HTML hebt ontvangen. Een blokje, een lege plek of een vraagteken dat je niet kunt selecteren betekent U+FEFF. Staat er niets tussen de aanhalingstekens, dan was er om te beginnen al geen invoer.
De oude en de nieuwe formulering
Zoekresultaten voor json parse unexpected token position 0 zijn meestal geschreven op basis van een oudere melding uit V8:
SyntaxError: Unexpected token in JSON at position 0
Die formulering noemde de offset en verborg het teken. De huidige doet het omgekeerde: hij toont het teken plus een stukje van de invoer, wat veel bruikbaarder is, maar het betekent ook dat de pagina waarop je belandt een runtime kan beschrijven die je helemaal niet gebruikt. Noemt jouw fout nog steeds een positie in plaats van een teken, dan zit je op een oudere engine. De diagnose hieronder verandert daar niet door.
2. Bevestig binnen tien seconden dat het een BOM is
Vier controles, ruwweg op volgorde van snelheid. Eén ervan is al genoeg voor uitsluitsel.
Bekijk de eerste drie bytes.
$ hexdump -C data.json | head -1
00000000 ef bb bf 7b 22 61 22 3a 31 7d |...{"a":1}|
ef bb bf vóór de 7b ({) is de BOM. De ... in de ASCII-kolom rechts is hexdump die toegeeft dat er niets afdrukbaars te tonen valt.
Vraag het aan file. Die zegt het rechtstreeks, en verandert bovendien compleet van mening over het bestandstype:
$ file data.json
data.json: Unicode text, UTF-8 (with BOM) text, with no line terminators
$ file clean.json
clean.json: JSON data
Controleer het eerste code point in Node.
const fs = require('fs');
const raw = fs.readFileSync('data.json', 'utf8');
console.log(raw.charCodeAt(0) === 0xFEFF); // true
Lees de statusbalk van je editor. VS Code toont UTF-8 with BOM rechtsonder, en als je erop klikt verschijnt Save with encoding. Precies dat labeltje is de reden dat het bestand er goed uitzag: je editor wist het en heeft er verder niets over gezegd.
Wil je op byteniveau naar iets kijken dat je lokaal niet kunt dumpen, plak het dan in de Base64 decoderen & encoderen. Een UTF-8 BOM vooraan een payload levert altijd een string op die met 77u/ begint, en dat is handig om in een logregel te herkennen.
3. Waar je BOM vandaan komt
De BOM verwijderen uit een bestand dat elk uur opnieuw door een buildstap wordt gegenereerd, is een oplossing met een houdbaarheid van één uur. De gebruikelijke veroorzakers:
- Excels Opslaan als → CSV UTF-8. Die is bewust zo gebouwd, het is geen bug, en sectie 7 legt uit waarom.
- Kladblok en andere Windows-editors die UTF-8 with BOM als aparte opslagoptie aanbieden, soms zelfs als standaard.
- VS Code, wanneer
files.encodingoputf8bomstaat, in je gebruikersinstellingen of vastgelegd in.vscode/settings.json, waar niemand kijkt. - Shell-redirectie in PowerShell.
>enOut-Fileschrijven in sommige PowerShell-versies standaard een BOM, en die standaard verschilt tussen de 5.x-lijn (alleen Windows) en de 6/7-lijn (multiplatform). Vertrouw hier niet op je geheugen: schrijf één bestand weg en controleer de eerste drie bytes met de commando’s uit sectie 2. - Zelfgeschreven exportcode. Elke writer die een UTF-8-encoder opzet zonder aan te geven of er een signature meemoet, erft de standaard die het framework nu eenmaal gekozen heeft, en die keuze viel niet overal hetzelfde uit. Oude exportpaden in .NET en Java leveren dit het vaakst op.
- Export-tools van databases en BI-pakketten, die vaak een BOM meesturen omdat hun belangrijkste afnemer een spreadsheet is.
Komt het bestand van een partner of een leverancier en kun je de producent niet veranderen, ga dan door naar sectie 4 en verwijder de BOM bij het inlezen. Komt het uit je eigen repository, dan geeft sectie 9 het duurzame antwoord.
4. De oplossing in JavaScript en Node
Hier zit de meeste verwarring, want het JavaScript-ecosysteem heeft niet één BOM-beleid. Het heeft er meerdere, en die spreken elkaar tegen. Zelfde bestand, zelfde runtime, gemeten op node v25.8.2:
| API | Gedrag bij een BOM | Daaropvolgende JSON.parse |
|---|---|---|
fetch → res.json() | verwijderd | slaagt |
fs.readFileSync(f, 'utf8') | behouden | mislukt |
new TextDecoder() (standaard) | verwijderd | slaagt |
new TextDecoder('utf-8', { ignoreBOM: true }) | behouden | mislukt |
require('./data.json') | verwijderd | n.v.t., al ingelezen |
import(..., { with: { type: 'json' } }) | verwijderd | n.v.t., al ingelezen |
Twee dingen volgen uit die tabel, en allebei kosten ze mensen hele middagen.
ignoreBOM doet het tegenovergestelde van wat de naam zegt
ignoreBOM: true betekent niet “negeer de BOM”. Het betekent “negeer de bijzondere betekenis van de BOM en behandel hem als een gewoon teken”. Juist de standaardwaarde false is degene die hem verwijdert. De naam beschrijft wat de decoder negeert, niet wat jij terugkrijgt, en wie hem op de voor de hand liggende manier leest, houdt een decoder over die precies de byte bewaart die hij wilde weggooien.
Waarom het in de browser werkt en in Node stukgaat
Dit is de variant die het vaakst opduikt: dezelfde JSON-URL laat zich in front-endcode prima als JSON inlezen, maar gooit een fout zodra een Node-script het bestand van schijf leest. Aan het bestand is niets veranderd. res.json() decodeert via dezelfde machinerie als TextDecoder en laat de BOM onderweg vallen; fs.readFileSync(path, 'utf8') is een getrouwe decodering die je elk teken teruggeeft dat in het bestand staat, U+FEFF incluis.
Dezelfde asymmetrie verklaart waarom require('./config.json') werkt en JSON.parse(fs.readFileSync('./config.json', 'utf8')) niet. De moduleloader van Node voor JSON verwijdert de BOM; de handmatige route doet dat niet.
De BOM verwijderen
const fs = require('fs');
const raw = fs.readFileSync('data.json', 'utf8');
const data = JSON.parse(raw.replace(/^/, ''));
Veranker het patroon met ^. Een globale vervanging zonder anker zou ook legitieme U+FEFF-tekens binnen in stringwaarden wissen, en dat is dataverlies in plaats van een oplossing.
Er is een stiller alternatief dat per ongeluk werkt: JSON.parse(raw.trim()) slaagt ook, omdat ECMAScript U+FEFF als witruimte aanmerkt en String.prototype.trim het dus weghaalt. Dat gedrag is echt en nagemeten, maar het is een toevalligheid van de JavaScript-specificatie en gaat niet op voor andere talen. str.strip() in Python laat een U+FEFF precies staan waar het stond.
Wil je bevestigen dat het opgeschoonde resultaat echt geldig is en niet alleen maar foutloos, plak het dan in de JSON Formatter online. Is de BOM eenmaal weg, dan blijven voor positie 0 alleen de gewone escapeproblemen over, die aan bod komen in JSON-strings escapen: tekens, stringify en valkuilen.
5. De oplossing in Python: utf-8-sig
Python is de enige runtime die het probleem bij naam noemt in de foutmelding. Open een bestand met BOM als gewone UTF-8 en json geeft je in één adem de diagnose én de oplossing:
JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)
Ben je op unexpected utf-8 bom gaan zoeken en hier beland, dan komt die string hiervandaan. De codec waar de melding naar wijst, leest de BOM als signature en gooit hem weg:
import json
with open('data.json', encoding='utf-8-sig') as f:
data = json.load(f)
utf-8-sig is veilig op bestanden zonder BOM. Hij verwijdert er een als die er is en gedraagt zich verder als gewone UTF-8, en dat maakt hem de juiste standaard voor elk bestand dat je niet zelf hebt gemaakt.
Bytes en tekst gedragen zich anders
Een asymmetrie die het kennen waard is, want daardoor lijkt de bug sporadisch op te treden:
import json
json.loads(open('data.json', 'rb').read()) # {'a': 1} werkt
json.loads(open('data.json', encoding='utf-8').read()) # gooit de fout hierboven
json.loads detecteert op bytes eerst de tekencodering, ziet de BOM en decodeert voor je met utf-8-sig. Geef je er een al gedecodeerde str aan, dan valt er niets meer te detecteren en bereikt de U+FEFF de parser. Twee codepaden die er gelijkwaardig uitzien, waarvan er één het probleem stilletjes voor je oplost.
Bewust een BOM wegschrijven
Dezelfde codec werkt ook de andere kant op, en zo maak je een bestand voor Excel:
with open('report.csv', 'w', encoding='utf-8-sig', newline='') as f:
f.write('name\n')
Dat bestand begint met ef bb bf. Sectie 7 behandelt wanneer je dat wilt.
De CSV-valkuil
csv.DictReader doet op tekst met BOM precies wat een correcte CSV-parser hoort te doen, en levert een sleutel op die nergens meer op past:
import csv, io
data = 'name,age\nAlice,30\n'
print(list(next(csv.DictReader(io.StringIO(data))).keys()))
# ['name', 'age']
Je eerste kolom heet niet name. Het is U+FEFF gevolgd door name, en elke lookup row['name'] gooit een KeyError, terwijl de header in elke debugger die je hebt keurig wordt afgedrukt. Open je het bestand met encoding='utf-8-sig', dan is de BOM al weg voordat de reader hem ooit ziet.
6. De BOM verwijderen in Java, Go, PHP en de shell
Elke oplossing is dezelfde oplossing op een ander niveau: verwijder drie bytes (EF BB BF) of verwijder één teken (U+FEFF), afhankelijk van of je bytes dan wel tekst in handen hebt. Heeft jouw taal geen codec die de BOM kent, doe het dan met de hand.
Java decodeert de BOM naar een -teken vooraan:
String text = Files.readString(path, StandardCharsets.UTF_8);
if (!text.isEmpty() && text.charAt(0) == '') {
text = text.substring(1);
}
Go, op byteniveau, vóór json.Unmarshal:
raw, err := os.ReadFile("data.json")
if err != nil {
return err
}
raw = bytes.TrimPrefix(raw, []byte{0xEF, 0xBB, 0xBF})
var v map[string]any
err = json.Unmarshal(raw, &v)
PHP, met een op bytes verankerd patroon:
$raw = file_get_contents('data.json');
$raw = preg_replace('/^\xEF\xBB\xBF/', '', $raw);
$data = json_decode($raw, true);
Wil je de BOM uit een bestand halen in plaats van uit een variabele, dan zijn dit vier commando’s die allemaal getest zijn op een bestand dat met ef bb bf begint:
# In place, GNU sed (Linux). De shell vult de escapes in, niet sed.
sed -i $'1s/^\xEF\xBB\xBF//' data.json
# In place, BSD sed (macOS)
sed -i '' $'1s/^\xEF\xBB\xBF//' data.json
# In place, overal waar Perl bestaat. Alleen de eerste regel.
perl -i -pe 's/^\x{ef}\x{bb}\x{bf}// if $. == 1' data.json
# Kopieer zonder de eerste drie bytes. Alleen veilig als je zeker weet dat er een BOM staat.
tail -c +4 data.json > clean.json
De tail-variant is de botte: die haalt drie bytes weg, of dat nu een BOM was of niet. Bevestig eerst met sectie 2.
7. De CSV-uitzondering: wanneer Excel de BOM nodig heeft
Alles hierboven behandelt de BOM als schade. Op één plek is hij dragend, en hem verwijderen maakt een werkend bestand stuk.
Zoekopdrachten op csv bom excel vallen uiteen in twee tegengestelde klachten, en dat is een goed teken dat één regel in de verkeerde richting wordt toegepast:
- “Mijn CSV opent in Excel met
éenæ¥æ¬èªin plaats van echte tekens.” De BOM ontbreekt. - “Mijn eerste kolom heet
nameen mijn script kan hem niet vinden.” De BOM is aanwezig.
Waarom Excel hem wil
Excel op Windows heeft geen betrouwbare manier om te weten dat een CSV in UTF-8 staat. Er is geen header, geen declaratie, geen metadata: een .csv-bestand is bytes. Zonder signaal valt Excel terug op de systeemlocale, dus Windows-1252 in de VS en West-Europa, Windows-1251 in Rusland, en dan komt elk niet-ASCII-teken verkeerd uit de bus. De BOM is dat signaal. Drie bytes vooraan en Excel leest UTF-8 correct.
Daarmee is de BOM in CSV een functie en geen defect, en dat levert een afweging op die in één regel past:
Geschreven om door een machine te worden ingelezen? Verwijder de BOM. Geschreven om door een mens in Excel te worden aangeklikt? Laat hem staan.
De storing aan de andere kant
Voer je hetzelfde bestand aan een parser, dan versmelt de BOM met je eerste headercel. In Node:
const header = 'name,age'.split(',');
console.log(JSON.stringify(header)); // ["name","age"]
const row = { 'name': 'Alice', age: 30 };
console.log(row.name); // undefined
row.name is undefined terwijl de sleutel in je logs, je debugger en je console.table gewoon als name wordt afgedrukt. Het is dezelfde soort bug als de KeyError in Python uit sectie 5, en daarom is het de moeite waard om “de veldnaam klopt maar de waarde ontbreekt” meteen als BOM-symptoom te lezen.
Onze eigen omzetters kiezen hier bewust elk een andere kant. De CSV naar JSON Omzetter verwijdert een BOM vooraan de invoer voordat er iets wordt ingelezen, zodat een bestand dat rechtstreeks uit Excel komt name oplevert en niet name. De andere kant op maakt de JSON naar CSV Omzetter de BOM tot een expliciete schakelaar, en de Excel-preset zet hem aan samen met een puntkomma als scheidingsteken en CRLF-regeleinden, precies de combinatie die Europese Excel-locales nodig hebben. Voor de bredere afwegingen rond scheidingstekens, aanhalingstekens en typeafleiding staat de volledige uitleg in CSV naar JSON omzetten: methoden, valkuilen & codevoorbeelden.
8. Verder dan JSON: waar een BOM nog meer opduikt
JSON maakt er lawaai over. Andere formaten niet.
Shellscripts. Een BOM zit tussen het begin van het bestand en de #!, waardoor de kernel nooit een shebang ziet en je interpreter dus nooit start. Op macOS was het gemeten resultaat dat de shell terugviel op sh en de shebang-regel als ontbrekend bestand meldde:
./bom.sh: line 1: #!/bin/sh: No such file or directory
Het script werd vervolgens alsnog uitgevoerd, onder de verkeerde interpreter, en dat is erger dan falen. Andere systemen verwoorden het anders, het bekendst als een bad interpreter-fout. Houdt een script dat met een volstrekt correcte #!/usr/bin/env python3 begint vol dat dat pad niet bestaat, controleer dan de bytes.
PHP. Alles buiten <?php ... ?> is uitvoer, en een BOM vóór de openingstag is drie bytes uitvoer die verstuurd worden voordat je code wordt uitgevoerd. De eerste aanroep van header(), session_start() of setcookie() mislukt dan met de klassieke waarschuwing headers already sent, met een verwijzing naar regel 1 van een bestand waarvan regel 1 er leeg uitziet.
.env-bestanden en elk ander formaat met sleutel-waardeparen lopen tegen hetzelfde mechanisme aan als CSV. Je eerste variabele heet niet DATABASE_URL, maar U+FEFF gevolgd door DATABASE_URL, dus de lookup mist terwijl het bestand voor een mens gewoon klopt. Alle volgende variabelen werken wel, waardoor het lijkt op een probleem met één specifieke instelling.
XML is de uitzondering in de andere richting. De XML-specificatie staat een UTF-8 BOM aan het begin van een document expliciet toe, als onderdeel van de automatische detectie van de tekencodering, en parsers moeten daarmee overweg kunnen. xml.etree.ElementTree in Python accepteerde in de test een document met BOM zonder morren. Gaat XML bij jou stuk, dan ligt dat waarschijnlijk niet aan de BOM.
9. Pak het bij de bron aan
Zodra je het mechanisme doorhebt, is de BOM uit een bestand halen het makkelijke deel. Voorkomen dat het bestand er weer een krijgt, is het deel dat blijft werken.
Leg de tekencodering vast in .editorconfig. De eigenschap charset kent utf-8 en utf-8-bom als aparte waarden, dus wie opschrijft welke hij wil, laat geen ruimte voor twijfel:
[*]
charset = utf-8
Controleer de editorinstelling die dat overschrijft. In VS Code is dat "files.encoding": "utf8", en de waarde waar je op moet letten is utf8bom. Kijk zowel in de .vscode/settings.json van de werkruimte als in je gebruikersinstellingen, want een vastgelegde werkruimte-instelling geldt stilzwijgend voor iedereen in het team.
Scan in CI of in een pre-commit hook. Dit werkt overal, heeft geen afhankelijkheden en eindigt met een exitcode ongelijk aan nul zodra het iets vindt:
#!/bin/sh
# Faal als een bestand in versiebeheer met EF BB BF begint
found=0
for f in $(git ls-files '*.json' '*.md' '*.sh'); do
if [ "$(head -c3 "$f" | od -An -tx1 | tr -d '[:space:]')" = "efbbbf" ]; then
echo "BOM: $f"
found=1
fi
done
exit $found
In beide richtingen geverifieerd: het somt de betreffende paden op en eindigt met 1 zodra er een bestand met BOM in versiebeheer staat, en met 0 zodra de bestanden schoon zijn.
Schrijf de ene toegestane uitzondering op. Een regel van “nergens een BOM” sneuvelt de eerste keer dat iemand een spreadsheet-export nodig heeft, en daarna wordt hij overal genegeerd. Benoem de uitzondering liever expliciet: BOM’s zijn toegestaan in CSV-bestanden die voor Excel gegenereerd worden, nergens anders. Sluit de exportmap uit van de scanner en de regel overleeft het contact met de werkelijkheid.
10. Een bisectie-workflow van zestig seconden
Doorloop de stappen op volgorde. Elke stap beëindigt het onderzoek of geeft de volgende stap een kleiner probleem in handen.
- Lees het teken, niet de positie. Sectie 1.
<betekent HTML en dan ben je hier klaar. Niets tussen aanhalingstekens betekent een lege body. Een onleesbaar blokje betekent doorgaan. - Bevestig de bytes.
hexdump -C file | head -1. Zijn de eerste drie bytes nietef bb bf, stop dan: dit is geen BOM en niets hieronder gaat helpen. - Zoek waar hij binnenkomt. Staat de BOM al op schijf vooraan het bestand, of is het bestand schoon op schijf en zit de BOM er pas in wanneer je code het vasthoudt? Is het op schijf schoon, dan voegt iets in je pipeline hem toe.
- Kies één kant om te repareren. Verwijder de BOM bij het inlezen wanneer de producent een leverancier is, een upload, of een buildstap die niet van jou is. Repareer de producent wanneer die wel van jou is, want de oplossing aan de leeskant moet je bij elke lezer opnieuw doorvoeren.
- Grijp in op de decodeergrens, niet dieper.
encoding='utf-8-sig'bij de aanroep vanopen(), niet een.lstrip()op een string drie functies verderop. Repareer je het diep in de stack, dan mag het volgende codepad dat het bestand leest de bug opnieuw ontdekken. - Controleer of de bytes veranderd zijn. Herhaal stap 2. Een oplossing die in één codepad werkt maar het bestand ongemoeid liet, gaat in het volgende alsnog stuk.
- Voeg de scanner toe. Sectie 9. Anders doe je dit volgend kwartaal allemaal opnieuw.
FAQ
Is een UTF-8 BOM verplicht?
Nee. UTF-8 heeft maar één bytevolgorde, dus er valt met een markering niets te verduidelijken. Unicode staat een UTF-8 BOM als signature van de tekencodering toe maar raadt hem niet aan, en JSON verbiedt hem ronduit: RFC 8259 stelt dat implementaties geen byte order mark aan een JSON-tekst mogen toevoegen.
Waarom ziet het bestand er in mijn editor prima uit maar laat het zich niet inlezen?
Omdat U+FEFF helemaal niets weergeeft. Editors die het teken herkennen, verbergen het en zetten in plaats daarvan UTF-8 with BOM in de statusbalk. Editors die het niet herkennen, tekenen nul pixels. Ook cat, less en een diff in code review zien er identiek uit. Alleen een blik op byteniveau legt hem bloot.
Verwijdert JSON.parse de BOM ooit automatisch?
Nooit. JSON.parse krijgt een string en behandelt U+FEFF als een onverwacht teken, waar het ook staat. Wat hem wél verwijdert, is de laag erboven: res.json() na een fetch, require() van Node voor .json-bestanden en TextDecoder met zijn standaardinstellingen halen hem alle drie weg voordat de parser iets ziet.
Moet ik de BOM uit CSV-bestanden verwijderen?
Dat hangt ervan af wie het bestand opent. Elke parser vouwt de BOM in je eerste kolomnaam, waardoor name verandert in name en elke lookup ernaast grijpt. Verwijder hem daar. Excel op Windows gebruikt de BOM juist om UTF-8 te herkennen en verhaspelt zonder hem alle letters met accenten en alle CJK-tekens, dus laat hem daar staan.
Is de BOM hetzelfde als een zero-width space?
Hetzelfde code point, een andere taak. U+FEFF op offset 0 is een byte order mark. Overal elders in een document is het ZERO WIDTH NO-BREAK SPACE, een gebruik dat Unicode heeft afgeraden ten gunste van U+2060 WORD JOINER. Oude tekst bevat het nog steeds, en daarom duikt U+FEFF ook middenin bestanden op.
Heeft de BOM invloed op git-diffs en bestandsgrootte?
Drie bytes op schijf, en één ruisregel in elke diff die het bestand raakt. Git vergelijkt bytes, dus een BOM toevoegen of weghalen herschrijft regel 1, ook als de weergegeven tekst identiek is. Daar komt die wijziging van één regel vandaan die niemand in de review kan verklaren.