wavetermdev/waveterm · error

file part missing both url and data

Error message

file part missing both url and data

What it means

ExtractImageUrl builds the image URL for a file message part. It throws this error when a file part has neither a usable URL nor any raw bytes, so there is no way to construct an image reference for the model. This guards the AI message conversion path from emitting empty/dead image parts.

Source

Thrown at pkg/aiusechat/aiutil/aiutil.go:82

	hash := hasher.Sum(nil)
	return hex.EncodeToString(hash)[:8]
}

// ExtractImageUrl extracts an image URL from either URL field (http/https/data) or raw Data
func ExtractImageUrl(data []byte, url, mimeType string) (string, error) {
	if url != "" {
		if !strings.HasPrefix(url, "data:") &&
			!strings.HasPrefix(url, "http://") &&
			!strings.HasPrefix(url, "https://") {
			return "", fmt.Errorf("unsupported URL protocol in file part: %s", url)
		}
		return url, nil
	}
	if len(data) > 0 {
		base64Data := base64.StdEncoding.EncodeToString(data)
		return fmt.Sprintf("data:%s;base64,%s", mimeType, base64Data), nil
	}
	return "", fmt.Errorf("file part missing both url and data")
}

// ExtractTextData extracts text data from either Data field or URL field (data: URLs only)
func ExtractTextData(data []byte, url string) ([]byte, error) {
	if len(data) > 0 {
		return data, nil
	}
	if url != "" {
		if strings.HasPrefix(url, "data:") {
			_, decodedData, err := utilfn.DecodeDataURL(url)
			if err != nil {
				return nil, fmt.Errorf("failed to decode data URL for text/plain file: %w", err)
			}
			return decodedData, nil
		}
		return nil, fmt.Errorf("dropping text/plain file with URL (must be fetched and converted to data)")
	}
	return nil, fmt.Errorf("text/plain file part missing data")

View on GitHub (pinned to a4447c1563)

Solutions

  1. Populate either the URL field (http/https/data:) or the raw Data bytes before building the message part.
  2. Fetch the file content and pass its bytes as data (it will be base64-encoded into a data: URL automatically).
  3. If the part is unrecoverable, drop the file part from the message instead of passing an empty one.
  4. Wrap the call in error handling and skip/replace the part with a text note explaining the attachment failed.

Example fix

// before
part := &uctypes.UIMessagePart{Type: "file", Data: uctypes.UIMessageDataUserFile{FileName: "img.png", MimeType: "image/png"}}
url, err := aiutil.ExtractImageUrl(nil, "", "image/png") // error
// after
fileData, _ := os.ReadFile("img.png")
url, err := aiutil.ExtractImageUrl(fileData, "", "image/png") // data:image/png;base64,...
Defensive patterns

Strategy: validation

Validate before calling

func validImagePart(data []byte, url string) bool {
    if len(data) > 0 { return true }
    return url != "" && (strings.HasPrefix(url, "data:") || strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://"))
}
// call ExtractImageUrl only if validImagePart(data, url)

Type guard

func hasImageSource(data []byte, url string) bool { return len(data) > 0 || url != "" }

Try / catch

url, err := aiutil.ExtractImageUrl(data, url, mime)
if err != nil {
    log.Printf("skipping image part: %v", err)
    return nil // drop or substitute part
}

Prevention

When it happens

Trigger: Calling ExtractImageUrl (via convertFileAIMessagePart/convertAIMessageMultimodal) with a UIMessageDataUserFile-style part where url == "" and len(data) == 0. Note: if url is set but uses an unsupported protocol, a different error (unsupported URL protocol) is returned instead.

Common situations: Constructing AI message parts programmatically and forgetting to read the file bytes into Data; a previously-attached file whose URL was revoked or never populated; serialization round-trips that drop the Data field.

Related errors


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