Skip to content

fix(tools): handle UTF-8 BOM in CSVLoader - #7467

Open
Jim-jimu wants to merge 2 commits into
crewAIInc:mainfrom
Jim-jimu:fix/csv-loader-utf8-bom
Open

Jim-jimu wants to merge 2 commits into
crewAIInc:mainfrom
Jim-jimu:fix/csv-loader-utf8-bom

Conversation

@Jim-jimu

@Jim-jimu Jim-jimu commented Sep 15, 2026

Copy link
Copy Markdown

Related issue

Fixes #7466

Summary

A leading UTF-8 BOM becomes part of CSVLoader's first column name. Before a quoted header containing a comma, it also prevents quote recognition and assigns row values to the wrong columns. URL responses with Content-Type: text/csv and no charset have an additional decoding problem: Requests defaults to Latin-1, corrupting both the BOM and non-ASCII values before CSV parsing.

The shared URL loader now recognizes a UTF-8 byte signature before reading response.text and selects utf-8-sig decoding. CSVLoader also removes one leading U+FEFF from decoded input before csv.DictReader parses the content. Responses without a UTF-8 BOM keep their existing encoding, and U+FEFF inside field values is preserved.

Regression tests cover file, inline-text and URL inputs, quoted/unquoted headers, BOM/no-BOM inputs, non-ASCII values, Latin-1 responses and embedded U+FEFF, plus JSON/XML compatibility with automatic and explicit BOM decoding. URL regressions use real requests.Response objects backed by bytes, with Requests' HTTP-header encoding inference.

Verification

  • Tests added for the changed behavior
  • Relevant tests and quality checks pass locally

Python 3.13.13, macOS 26.6.2:

  • uv run --locked pytest lib/crewai-tools/tests/rag/test_csv_loader.py -n 0 -q: 30 passed. The expanded byte-backed suite produced 4 failed, 26 passed before the URL decoding fix, reproducing the review finding.
  • uv run --locked pytest lib/crewai-tools/tests/rag/test_csv_loader.py lib/crewai-tools/tests/rag/test_loader_utils.py lib/crewai-tools/tests/rag/test_json_loader.py lib/crewai-tools/tests/rag/test_mdx_loader.py lib/crewai-tools/tests/rag/test_xml_loader.py -n 0 -q: 68 passed.
  • Ruff lint and format checks pass for the changed files, including an explicit check of the normally excluded test file.
  • uv run --locked mypy lib/crewai-tools/src/crewai_tools/rag/loaders/utils.py lib/crewai-tools/src/crewai_tools/rag/loaders/csv_loader.py: no issues found.

The test runs emit one existing crewai.utilities.lock_store deprecation warning.

Additional context

This contribution was prepared with Codex assistance. Per CONTRIBUTING.md, please apply the llm-generated label; this external contributor account does not have permission to add labels.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-15T06:52:32.528097Z 0e5075d New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 15, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: d27f2151-8eae-4be2-a0a2-13f4c52a5078

📥 Commits

Reviewing files that changed from the base of the PR and between 3e980c4 and 0e5075d.

📒 Files selected for processing (3)
  • lib/crewai-tools/src/crewai_tools/rag/loaders/utils.py
  • lib/crewai-tools/tests/rag/test_csv_loader.py
  • lib/crewai-tools/tests/rag/test_loader_utils.py

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

CSVLoader removes one leading UTF-8 BOM before parsing headers. URL loading detects UTF-8 BOM bytes and selects utf-8-sig. Tests cover CSV, JSON, and XML loaders.

Changes

BOM handling

Layer / File(s) Summary
URL BOM decoding
lib/crewai-tools/src/crewai_tools/rag/loaders/utils.py, lib/crewai-tools/tests/rag/test_loader_utils.py
URL loading sets utf-8-sig when response content starts with a UTF-8 BOM. Tests verify BOM decoding for JSON and XML sources.
CSV BOM normalization and coverage
lib/crewai-tools/src/crewai_tools/rag/loaders/csv_loader.py, lib/crewai-tools/tests/rag/test_csv_loader.py
_parse_csv removes one leading BOM before csv.DictReader processes the content. Tests cover text, file, and URL inputs, header formats, encodings, and BOM preservation inside field values.

Suggested reviewers: thecybertech

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 0e507

The BOM normalization changes have no identified merge-blocking issue.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The change meets the coding requirements in issue #7466. CSVLoader._parse_csv removes one leading U+FEFF before csv.DictReader parses the content. This fixes quoted headers with commas, removes …
Out of Scope Changes check ✅ Passed The changes stay within issue #7466. The load_from_url update supports correct BOM handling for the required URL input path. The JSON and XML utility tests protect this shared decoding behavior. The…
Title check ✅ Passed The title clearly and concisely describes the main change: adding UTF-8 BOM handling to CSVLoader.
Description check ✅ Passed The description includes the required issue link, explains the problem and solution, lists verification steps and results, and provides additional context. It is complete and relevant.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e980c4462

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

def _parse_csv(self, content: str, source_ref: str) -> LoaderResult:
try:
csv_reader = csv.DictReader(StringIO(content))
csv_reader = csv.DictReader(StringIO(content.removeprefix("\ufeff")))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Decode URL responses before stripping the BOM

When a URL serves BOM-prefixed UTF-8 bytes as Content-Type: text/csv without a charset, Requests assigns ISO-8859-1 before load_from_url() reads response.text, so this method receives name,... rather than a leading U+FEFF. The new removeprefix() is therefore a no-op and the first header remains corrupted; the URL test masks this by mocking .text as already-correct Unicode. Detect the raw BOM and select utf-8-sig before reading the response text, and exercise that behavior with a byte-backed response.

AGENTS.md reference: AGENTS.md:L10-L11

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Reproduced with a real requests.Response containing UTF-8 BOM bytes and Content-Type: text/csv without a charset. Requests selected Latin-1, and four byte-backed CSV regression cases failed on the previous implementation.

Fixed in 0e5075d. The shared load_from_url() helper now detects BOM_UTF8 in response.content and selects utf-8-sig before reading response.text.

The URL tests now exercise real response decoding and cover quoted/unquoted headers, non-ASCII values, BOM-free UTF-8 and Latin-1 responses, and embedded U+FEFF. Additional JSON/XML tests preserve compatibility with automatic and explicit BOM decoding in other users of the shared helper.

Validation: 68 tests passed, including 30 CSV tests; Ruff lint/format and mypy passed. The only warning is the existing crewai.utilities.lock_store deprecation warning.

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.

[BUG] CSVLoader misparses headers in UTF-8 CSV files with a BOM

1 participant