wavetermdev/waveterm · error

dropping PDF with URL (must be fetched and converted to base

Error message

dropping PDF with URL (must be fetched and converted to base64 data)

What it means

OpenAI's API only accepts PDFs as base64-encoded data, never as URLs. The converter found a PDF part with a URL but no inline Data, and returns this error instead of silently sending an unsupported URL-based PDF.

Source

Thrown at pkg/aiusechat/openai/openai-convertmessage.go:351

	switch {
	case strings.HasPrefix(part.MimeType, "image/"):
		imageUrl, err := aiutil.ExtractImageUrl(part.Data, part.URL, part.MimeType)
		if err != nil {
			return nil, err
		}

		return &OpenAIMessageContent{
			Type:       "input_image",
			ImageUrl:   imageUrl,
			Filename:   part.FileName,
			PreviewUrl: part.PreviewUrl,
		}, nil

	case part.MimeType == "application/pdf":
		// Handle PDFs - OpenAI only supports base64 data for PDFs, not URLs
		if len(part.Data) == 0 {
			if part.URL != "" {
				return nil, fmt.Errorf("dropping PDF with URL (must be fetched and converted to base64 data)")
			}
			return nil, fmt.Errorf("PDF file part missing data")
		}

		// Convert raw data to base64
		base64Data := base64.StdEncoding.EncodeToString(part.Data)

		return &OpenAIMessageContent{
			Type:       "input_file",
			Filename:   part.FileName, // Optional filename
			FileData:   base64Data,
			PreviewUrl: part.PreviewUrl,
		}, nil

	case part.MimeType == "text/plain":
		textData, err := aiutil.ExtractTextData(part.Data, part.URL)
		if err != nil {
			return nil, err

View on GitHub (pinned to a4447c1563)

Solutions

  1. Fetch the PDF yourself, put the raw bytes into part.Data, and clear part.URL
  2. Base64-encode is handled by the converter — only raw bytes are needed in Data
  3. Pre-download attachments in your ingestion pipeline before constructing message parts

Example fix

// before
part := uctypes.AIMessagePart{Type: "file", MimeType: "application/pdf", URL: "https://cdn.example.com/doc.pdf"}
// after
data, _ := http.Get(part.URL) // fetch first
body, _ := io.ReadAll(data.Body)
part := uctypes.AIMessagePart{Type: "file", MimeType: "application/pdf", Data: body}
Defensive patterns

Strategy: validation

Validate before calling

func validatePDFPart(p uctypes.AIMessagePart) error {
    if p.MimeType != "application/pdf" { return nil }
    if len(p.Data) == 0 && p.URL != "" {
        return errors.New("PDF has URL only; fetch bytes into Data first")
    }
    return nil
}

Type guard

func hasInlinePDFData(p uctypes.AIMessagePart) bool {
    return p.MimeType == "application/pdf" && len(p.Data) > 0
}

Prevention

When it happens

Trigger: Building a file part with MimeType "application/pdf", part.URL set to a web/CDN link, and part.Data empty; passing through attachment metadata from another provider (e.g. Anthropic/Claude-style URL PDFs) directly to the OpenAI backend.

Common situations: Migrating chat pipelines from Claude (which accepts PDF URLs) to OpenAI; storing files behind signed URLs and referencing them by link; frontend sending upload URLs instead of downloaded bytes.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/b17d252a606c6966. Report an issue: GitHub.