Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

fix: catch UnicodeDecodeError in _is_json to avoid crashing detect_filetype()#4398

Open
chuenchen309 wants to merge 2 commits into
Unstructured-IO:mainUnstructured-IO/unstructured:mainfrom
chuenchen309:fix/is-json-unicode-decode-crashchuenchen309/unstructured:fix/is-json-unicode-decode-crashCopy head branch name to clipboard
Open

fix: catch UnicodeDecodeError in _is_json to avoid crashing detect_filetype()#4398
chuenchen309 wants to merge 2 commits into
Unstructured-IO:mainUnstructured-IO/unstructured:mainfrom
chuenchen309:fix/is-json-unicode-decode-crashchuenchen309/unstructured:fix/is-json-unicode-decode-crashCopy head branch name to clipboard

Conversation

@chuenchen309

@chuenchen309 chuenchen309 commented Jul 14, 2026

Copy link
Copy Markdown

Problem

_TextFileDifferentiator._is_json calls json.load() directly on the raw file bytes when libmagic guesses a generic text/plain MIME type and the content starts with [/{:

try:
    with self._ctx.open() as file:
        json.load(file)
    return True
except json.JSONDecodeError:
    return False

If the bytes aren't valid UTF-8, json.load() raises UnicodeDecodeError (via its internal detect_encoding step) — a different exception type than json.JSONDecodeError, so it's not caught. It propagates straight out of the public detect_filetype() API instead of falling back to FileType.TXT, the behavior for any other content libmagic can't identify more specifically.

Repro

import io
from unstructured.file_utils.filetype import detect_filetype

data = b"{not json at all just braces \xe9 text} and more padding"
detect_filetype(file=io.BytesIO(data), metadata_file_path="filename.pdf")
# UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe9 in position 29: invalid continuation byte

Fix

Broaden the except clause to also catch UnicodeDecodeError.

Testing

  • Added test_it_does_not_crash_on_non_utf8_bracket_prefixed_text_with_no_extension, confirming detect_filetype() returns FileType.TXT instead of raising. Confirmed it fails against the pre-fix code and passes after.
  • Ran the full test_unstructured/file_utils/ suite (380 passed, 1 xfailed — no regressions).
  • ruff check/ruff format --check clean. Bumped unstructured/__version__.py and added a CHANGELOG.md entry per the contribution checklist.

Note: this bumps to the same 0.25.1 as my other open PR (#4397), both branched from the same base — whichever merges second will likely need a quick rebase on the version files, happy to handle that when the time comes.

Disclosure: I used an AI coding assistant (Claude) to help identify this bug and draft the fix. I independently reproduced the crash before writing the regression test and ran the full local test suite before opening this PR.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files

Shadow auto-approve: would not auto-approve because issues were found.

Re-trigger cubic

Comment thread test_unstructured/file_utils/test_filetype.py Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 1 file (changes from recent commits).

Shadow auto-approve: would auto-approve. Minimal bug fix: catches UnicodeDecodeError in _is_json to prevent crash on non-UTF-8 bracket-prefixed text. Includes a regression test and changelog.

Re-trigger cubic

chuenchen309 and others added 2 commits July 16, 2026 05:59
…letype()

_TextFileDifferentiator._is_json calls json.load() directly on the raw
file bytes when libmagic guesses a generic text/plain MIME type and the
content starts with `[`/`{`. If the bytes aren't valid UTF-8, json.load()
raises UnicodeDecodeError (via its internal detect_encoding step), which
wasn't caught by the existing `except json.JSONDecodeError`, so it
propagated straight out of the public detect_filetype() API instead of
falling back to FileType.TXT like any other undetectable content.

Broaden the except clause to also catch UnicodeDecodeError.

Added a regression test with non-UTF-8 bytes wrapped in braces and no
file extension, confirming detect_filetype() returns FileType.TXT
instead of raising. Confirmed it fails against the pre-fix code and
passes after. Ran the full test_unstructured/file_utils/ suite (380
passed, 1 xfailed -- no regressions), ruff check/format, and bumped
__version__.py + CHANGELOG.md per the contribution checklist (note:
this may create a merge conflict with my other open PR Unstructured-IO#4397, which
also bumps to 0.25.1 from the same base -- happy to rebase whichever
merges second).

Disclosure: I used an AI coding assistant (Claude) to help identify
this bug and draft the fix. I independently reproduced the
UnicodeDecodeError crash before writing the test and ran the full
local test suite before opening this PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…test

Addresses cubic-dev-ai review feedback: the test was named
"...with_no_extension" but passed metadata_file_path="filename.pdf",
so it didn't actually exercise the no-extension code path (extension
would resolve to ".pdf", not "").

Drop metadata_file_path entirely — an io.BytesIO with no .name
attribute makes `_FileTypeDetectionContext.extension` resolve to ""
(genuinely no extension), matching what the test name claims.
Behavior is unchanged either way since ".pdf" isn't in
_TextFileDifferentiator's special-cased extension list, but the test
now actually asserts what it says it does.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chuenchen309
chuenchen309 force-pushed the fix/is-json-unicode-decode-crash branch from 1840cad to 0a1fd19 Compare July 15, 2026 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Morty Proxy This is a proxified and sanitized view of the page, visit original site.