Skip to content

fix: handle non-image media types in Claude relay to prevent nil pointer panic#3531

Open
Panniantong wants to merge 1 commit intoQuantumNous:mainfrom
Panniantong:fix/claude-pdf-nil-panic
Open

fix: handle non-image media types in Claude relay to prevent nil pointer panic#3531
Panniantong wants to merge 1 commit intoQuantumNous:mainfrom
Panniantong:fix/claude-pdf-nil-panic

Conversation

@Panniantong
Copy link
Copy Markdown

@Panniantong Panniantong commented Apr 1, 2026

Summary

  • Add ContentTypeFile handling in RequestOpenAI2ClaudeMessage before the generic image branch
  • Convert PDF/text files to Claude document blocks instead of treating them as images
  • Add nil guard for GetImageMedia() as safety fallback for other unknown content types

Problem

When users upload PDF files via OpenAI-compatible clients (e.g., CherryStudio), the request contains type: "file" content blocks. The current code treats all non-text content as images and calls GetImageMedia(), which returns nil for file types. Accessing imageUrl.Url on the nil pointer causes a panic crash.

Root Cause

relay-claude.go:~352 — the else branch after type == "text" assumes everything else is an image, but ContentTypeFile (PDF/documents) has no ImageUrl field, so GetImageMedia() returns nil.

Fix

  1. New ContentTypeFile branch: Intercepts file-type content before the image handler. Uses GetFile() to extract file data, then converts to Claude's document block format (for PDF/text) or image block (for other file types).
  2. Nil guard on GetImageMedia(): Added if imageUrl == nil { continue } in the existing image branch as a safety net for any other unhandled content types.

Reference

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Expanded Claude messaging to support file uploads, including PDFs, text documents, and image files with intelligent format classification for optimal processing
  • Bug Fixes

    • Strengthened media content validation to properly identify and skip unsupported content types
    • Enhanced error handling with improved reporting when file conversion operations fail

Handle ContentTypeFile (PDF/document) separately from image content
in RequestOpenAI2ClaudeMessage. Previously, all non-text content was
treated as image, causing nil pointer dereference when GetImageMedia()
returned nil for file/document types.

- Add ContentTypeFile branch before the generic image handler
- Convert PDF/text files to Claude document blocks
- Add nil guard for GetImageMedia() as safety fallback

Fixes QuantumNous#3481
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

Walkthrough

The change extends media-content parsing in Claude relay to handle file uploads by extracting file payloads, converting them to base64, and classifying them as documents or images based on MIME type. It adds defensive nil checks to prevent nil pointer dereference panics when processing non-image media content.

Changes

Cohort / File(s) Summary
Claude Relay File Support
relay/channel/claude/relay-claude.go
Added support for ContentTypeFile media segments with base64 encoding, document vs. image type classification, nil safety checks for image media, and error handling for file conversion failures.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • seefs001

Poem

🐰 A file hops in, no panic today,
Base64 encoded along the way,
PDFs and images both find their place,
Nil guards protect this relay race!
Claude channels forward with confident stride.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding nil-safety handling for non-image media types in Claude relay to prevent panic.
Linked Issues check ✅ Passed The PR implementation fully addresses all coding requirements from issue #3481: adds ContentTypeFile handling, implements nil guard for GetImageMedia(), and converts files to appropriate content types.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked issue requirements; no unrelated modifications or out-of-scope additions detected in the relay-claude.go file.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@relay/channel/claude/relay-claude.go`:
- Around line 361-371: The code currently always treats file.FileData as base64
(using types.NewBase64FileSource) and classifies anything not PDF/text as
"image", which breaks for URL-backed file_data and non-image MIME types; update
the logic in the block that builds fileSource and sets claudeMediaMessage.Type
to first detect if file.FileData is a URL (e.g.,
strings.HasPrefix(file.FileData, "http://") or "https://") and create a URL file
source (use the library's URL file source constructor instead of
types.NewBase64FileSource) so service.GetBase64Data can fetch/convert it, then
inspect the returned mimeType and gate on it strictly: set
claudeMediaMessage.Type = "image" only when strings.HasPrefix(mimeType,
"image/"), set "document" for "application/pdf" or strings.HasPrefix(mimeType,
"text/"), and otherwise return an error or skip building the media block to
avoid sending invalid upstream requests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef76cee3-dceb-44c9-9b55-9c3145af90db

📥 Commits

Reviewing files that changed from the base of the PR and between 5b47011 and 36954f3.

📒 Files selected for processing (1)
  • relay/channel/claude/relay-claude.go

Comment on lines +361 to +371
fileSource := types.NewBase64FileSource(file.FileData, "")
base64Data, mimeType, err := service.GetBase64Data(c, fileSource, "formatting file for Claude")
if err != nil {
return nil, fmt.Errorf("get file data failed: %s", err.Error())
}
// PDF and text documents use "document" type, others use "image"
if strings.HasPrefix(mimeType, "application/pdf") || strings.HasPrefix(mimeType, "text/") {
claudeMediaMessage.Type = "document"
} else {
claudeMediaMessage.Type = "image"
}
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.

⚠️ Potential issue | 🟠 Major

Handle URL-backed file_data and MIME gating before building Claude media blocks.

At Line 361, file.FileData is always treated as base64. If the client sends a URL payload, conversion fails. Also, at Lines 367-371, any non-PDF/non-text file is labeled as "image" even when MIME is not image/*, which can produce invalid upstream requests.

💡 Suggested fix
-						fileSource := types.NewBase64FileSource(file.FileData, "")
+						if strings.TrimSpace(file.FileData) == "" {
+							continue
+						}
+						var fileSource *types.FileSource
+						if strings.HasPrefix(file.FileData, "http://") || strings.HasPrefix(file.FileData, "https://") {
+							fileSource = types.NewURLFileSource(file.FileData)
+						} else {
+							fileSource = types.NewBase64FileSource(file.FileData, "")
+						}
 						base64Data, mimeType, err := service.GetBase64Data(c, fileSource, "formatting file for Claude")
 						if err != nil {
 							return nil, fmt.Errorf("get file data failed: %s", err.Error())
 						}
-						// PDF and text documents use "document" type, others use "image"
+						// PDF/text -> document; image/* -> image; others unsupported
 						if strings.HasPrefix(mimeType, "application/pdf") || strings.HasPrefix(mimeType, "text/") {
 							claudeMediaMessage.Type = "document"
-						} else {
+						} else if strings.HasPrefix(mimeType, "image/") {
 							claudeMediaMessage.Type = "image"
+						} else {
+							continue
 						}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@relay/channel/claude/relay-claude.go` around lines 361 - 371, The code
currently always treats file.FileData as base64 (using
types.NewBase64FileSource) and classifies anything not PDF/text as "image",
which breaks for URL-backed file_data and non-image MIME types; update the logic
in the block that builds fileSource and sets claudeMediaMessage.Type to first
detect if file.FileData is a URL (e.g., strings.HasPrefix(file.FileData,
"http://") or "https://") and create a URL file source (use the library's URL
file source constructor instead of types.NewBase64FileSource) so
service.GetBase64Data can fetch/convert it, then inspect the returned mimeType
and gate on it strictly: set claudeMediaMessage.Type = "image" only when
strings.HasPrefix(mimeType, "image/"), set "document" for "application/pdf" or
strings.HasPrefix(mimeType, "text/"), and otherwise return an error or skip
building the media block to avoid sending invalid upstream requests.

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.

Claude 转发处理非图片媒体内容时 panic: nil pointer dereference (relay-claude.go:359)

1 participant