You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A BOM-prefixed JSON document is reported as JSON, where file reports Unicode text #3500
PolyFile reports a UTF-8 BOM followed by a JSON document as JSON. file does not, because its JSON
parser works on bytes and the BOM is not valid JSON syntax.
Reproducer
$ printf'\xef\xbb\xbf{}'> bom.json
$ TZ=UTC ./file/src/file -b -m file/magic/magic.mgc bom.jsonUnicode text, UTF-8 (with BOM) text, with no line terminators
$ polyfile bom.jsonJSON text data
Cause
PolyFile's JSON test decodes the buffer with Python's json machinery, which strips a UTF-8 BOM as
part of its encoding detection, so the BOM never reaches the parser. libmagic's file_is_json
(file/src/is_json.c:421-455) advances a byte pointer and rejects the first byte outright.
This predates #3486 and survives it: the newline-delimited JSON work replaced the whole-buffer json.loads with a raw_decode loop but kept Python's encoding detection, so the divergence is
unchanged.
Worth deciding, not just fixing
Which behavior is correct here is a judgment call, so decide it before changing anything:
RFC 8259 section 8.1 says a JSON implementation must not add a BOM and may ignore one when
parsing. Both tools are within spec.
PolyFile's default output mimics file, which argues for matching it.
Against that, a BOM-prefixed JSON file is JSON in practice, and several editors write one. Calling
it plain Unicode text is arguably less useful, even if it is what file does.
Whichever way it goes, the behavior deserves a test, since right now it is incidental to a library's
encoding detection rather than chosen.
Impact
One class of input reports a different type from file. No corpus stem covers it. Found while
verifying #3486.
This is broader than a BOM. Python's json encoding detection also handles UTF-16 and UTF-32, so
PolyFile reports JSON for inputs libmagic does not consider JSON at all.
$ python3 -c "import pathlib; pathlib.Path('u16.json').write_bytes('{\"a\": 1}\n'.encode('utf-16-le'))"
$ TZ=UTC ./file/src/file -b -k -r -m file/magic/magic.mgc u16.jsonUnicode text, UTF-16, little-endian text
$ polyfile u16.jsonJSON text data
libmagic's file_is_json walks raw bytes, so UTF-16-encoded JSON is simply not JSON to it — the
same reason a UTF-8 BOM disqualifies a document there. parse_json decodes with json.detect_encoding first, which accepts all of UTF-8-with-BOM, UTF-16 and UTF-32.
So the decision this issue asks for is one decision, not two: whether PolyFile's JSON test should
work on bytes the way libmagic's does, or keep Python's encoding detection and diverge for every
encoding it recognizes. Worth settling once rather than per-encoding.
Confirmed pre-existing rather than introduced by recent work: master and the #3489 branch both
report JSON text data for the file above, and #3489's precedes_soft_magic change deliberately
keeps the JSON and CSV checks on the file's own bytes, so the decoded text buffer is not involved.
This issue asked for the behavior to be decided before anything changed. Decided: emit the JSON
match and let the text-encoding machinery also report Unicode text. Neither answer is lost.
The reasoning is that a BOM-prefixed JSON file is JSON in practice — several editors write one — and
PolyFile's model is to report every match rather than pick a winner. Suppressing the JSON match to
mirror file would throw away the more useful of the two facts. RFC 8259 §8.1 permits either
reading, so both tools stay in spec.
This does diverge from file, which reports only Unicode text, UTF-8 (with BOM) text. That is a
deliberate difference, not a defect, and the test should say so.
Fix site
parse_json at polyfile/magic.py:3700-3737. The single line is :3723:
json.detect_encoding is what strips the BOM before the parser ever sees it. The docstring bullet
list at :3707-3715 documents libmagic's rules and wants a third bullet recording this decision.
JSONTest is at :3739-3784 (test at :3751-3763, precedes_soft_magic at :3768-3779); NDJSONTest at :3787-3790. Check whether precedes_soft_magic and appends_text_encoding need
adjusting so the text-encoding description actually reaches the output alongside the JSON match —
that is the part that makes "report both" real rather than nominal.
Verification
Add the test at around tests/test_magic.py:429, beside the existing JSON cases
(test_newline_delimited_json:381, test_single_json_value:391, test_json_values_must_share_a_first_byte:409, test_bare_json_scalar_is_not_json:420).
Pin all three of: the JSON match is present, the Unicode text description is present, and what file reports for the same bytes.
Cover UTF-8, UTF-16 and UTF-32 BOMs — json.detect_encoding handles all of them.
Activity
ESultanik commentedon Sep 10, 2026
This is broader than a BOM. Python's
jsonencoding detection also handles UTF-16 and UTF-32, soPolyFile reports JSON for inputs libmagic does not consider JSON at all.
libmagic's
file_is_jsonwalks raw bytes, so UTF-16-encoded JSON is simply not JSON to it — thesame reason a UTF-8 BOM disqualifies a document there.
parse_jsondecodes withjson.detect_encodingfirst, which accepts all of UTF-8-with-BOM, UTF-16 and UTF-32.So the decision this issue asks for is one decision, not two: whether PolyFile's JSON test should
work on bytes the way libmagic's does, or keep Python's encoding detection and diverge for every
encoding it recognizes. Worth settling once rather than per-encoding.
Confirmed pre-existing rather than introduced by recent work: master and the #3489 branch both
report
JSON text datafor the file above, and #3489'sprecedes_soft_magicchange deliberatelykeeps the JSON and CSV checks on the file's own bytes, so the decoded text buffer is not involved.
ESultanik commentedon Sep 11, 2026
Decision: report both
This issue asked for the behavior to be decided before anything changed. Decided: emit the JSON
match and let the text-encoding machinery also report Unicode text. Neither answer is lost.
The reasoning is that a BOM-prefixed JSON file is JSON in practice — several editors write one — and
PolyFile's model is to report every match rather than pick a winner. Suppressing the JSON match to
mirror
filewould throw away the more useful of the two facts. RFC 8259 §8.1 permits eitherreading, so both tools stay in spec.
This does diverge from
file, which reports onlyUnicode text, UTF-8 (with BOM) text. That is adeliberate difference, not a defect, and the test should say so.
Fix site
parse_jsonatpolyfile/magic.py:3700-3737. The single line is:3723:json.detect_encodingis what strips the BOM before the parser ever sees it. The docstring bulletlist at
:3707-3715documents libmagic's rules and wants a third bullet recording this decision.JSONTestis at:3739-3784(testat:3751-3763,precedes_soft_magicat:3768-3779);NDJSONTestat:3787-3790. Check whetherprecedes_soft_magicandappends_text_encodingneedadjusting so the text-encoding description actually reaches the output alongside the JSON match —
that is the part that makes "report both" real rather than nominal.
Verification
Add the test at around
tests/test_magic.py:429, beside the existing JSON cases(
test_newline_delimited_json:381,test_single_json_value:391,test_json_values_must_share_a_first_byte:409,test_bare_json_scalar_is_not_json:420).Pin all three of: the JSON match is present, the Unicode text description is present, and what
filereports for the same bytes.Cover UTF-8, UTF-16 and UTF-32 BOMs —
json.detect_encodinghandles all of them.