GEMINI LABJP
NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20
Articles/API / SDK
API / SDK/2026-04-30Intermediate

Why Gemini Says It Cannot See Your Image — A Practical Diagnosis Guide

If Gemini API replies 'I don't see an image' despite an attached file, the cause is almost always client-side. This guide walks through the four checks — mime_type, payload size, SDK version, and model selection — with copy-pasteable fixes.

gemini-api277troubleshooting82multimodal44image5python104

"As an AI, I cannot see images." "I don't see an attachment in your message." If you have ever tried multimodal input with Gemini API, chances are you have hit one of these replies and stared at the screen wondering what went wrong. The image is right there in your code. The model is multimodal. And yet the response insists nothing was attached.

I lost an entire afternoon to this exact problem when building a product-image analyzer. The cause is almost never on Gemini's side — it is in how the request is constructed on the client. Once you know the four common pitfalls and the order in which to check them, the issue is usually a one-line fix.

First, suspect that the image never arrived

Even when the error message reads like "I cannot see images," the most common reality is that the image bytes never reached the API. When Gemini sees a request with no image part, it answers honestly in plain text: there is no image here. The model is not lying — your client code is silently producing a text-only request.

Three things to verify before anything else:

  • The contents array contains both the image part and a mime_type.
  • The image is 20 MB or less (the inline upload limit, which counts the entire request body).
  • You are using a multimodal-capable modelgemini-2.5-flash or gemini-2.5-pro, not text-only models like text-embedding-004.

In my experience, finding a missing mime_type here resolves the issue more than half the time. The remaining cases split roughly evenly between size limits and SDK version mismatches, which we will work through next.

Pitfall 1: mime_type is not auto-detected

Even with the new unified google-genai SDK, if you pass an image as raw bytes, the API will not infer the mime type from the file extension. The SDK does not peek at the first bytes of the file either. Do not assume detection — always be explicit.

# ❌ Common mistake: passing raw bytes
from google import genai
from pathlib import Path
 
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
image_bytes = Path("product.jpg").read_bytes()
 
# This may return "I don't see any image."
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=["Describe the product features.", image_bytes],
)

The fix is to wrap the bytes in a Part and set mime_type explicitly. Note that the mime type is the content type, not the file extension — image/jpeg, not jpg.

# ✅ Correct: build a Part with explicit mime_type
from google import genai
from google.genai import types
from pathlib import Path
 
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
image_bytes = Path("product.jpg").read_bytes()
 
image_part = types.Part.from_bytes(
    data=image_bytes,
    mime_type="image/jpeg",  # use "image/png", "image/webp" as needed
)
 
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=["Describe the product features.", image_part],
)
print(response.text)
# Expected output (example):
# A leather shoulder bag sized for A4 documents, brown, with gold hardware…

Passing a PIL.Image directly is officially supported, but it requires pillow and depends on the image being well-formed. A subtle gotcha: if the file extension is .jpg but the actual encoding is HEIC (common on iPhones), Image.open may succeed yet produce data the API cannot parse. For production code, reading bytes directly and setting mime_type to match the actual encoding is the safer default.

Pitfall 2: Above 20 MB you must switch to the Files API

Inline image uploads only work when the entire request — text and image combined — stays under 20 MB. High-resolution phone photos and rasterized multi-page PDFs blow past this limit easily. A 12-megapixel JPEG can be 4–6 MB, so two of them plus a long instruction will already crowd the limit.

When the limit is exceeded, the request fails with 400 Bad Request or 413 Payload Too Large. The model is not refusing to look at the image; the bytes never reach it. This failure looks deceptively similar to the "image not seen" error because in both cases there is no image in the response, but the diagnosis differs — check your request status code first.

# ✅ For files >20 MB or many images, use the Files API
from google import genai
from google.genai import types
 
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
 
uploaded = client.files.upload(file="large_photo.jpg")
print(f"uploaded: name={uploaded.name}, uri={uploaded.uri}, state={uploaded.state.name}")
# Expected output:
# uploaded: name=files/abc123, uri=https://generativelanguage.googleapis.com/v1beta/files/abc123, state=ACTIVE
 
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=[
        "List three notable features of the subject in the photo.",
        uploaded,  # File object goes directly into contents
    ],
)
print(response.text)

The Files API gives you 20 GB of project-level storage even on the free tier, with files auto-deleted after 48 hours. For non-image media (video, PDF), uploaded files start in PROCESSING state — wait until ACTIVE before generating, or you will get inconsistent results. Images are usually ACTIVE immediately, but it is still good hygiene to check uploaded.state.name == "ACTIVE" before passing the file to generate_content.

For deeper coverage of inline vs. Files API trade-offs, see Gemini API Files API: A Complete Walkthrough and the dedicated Gemini API Files API Troubleshooting Guide.

Pitfall 3: The old and new SDKs work differently

In 2025, Google migrated from the legacy google-generativeai SDK to the unified google-genai package. The image-input syntax changed in subtle ways. Pasting code from older blog posts often produces no error — yet the model replies "I cannot see the image."

The legacy google-generativeai style looked like this:

# Legacy SDK — kept here for reference; not recommended for new projects
import google.generativeai as genai
from PIL import Image
 
genai.configure(api_key="YOUR_GEMINI_API_KEY")
model = genai.GenerativeModel("gemini-1.5-flash")
img = Image.open("product.jpg")
response = model.generate_content(["Describe this.", img])

Run this in a google-genai environment and one of two things happens: the import fails outright, or the call succeeds yet behaves as if no image was attached. The new SDK uses client.models.generate_content() plus types.Part.from_bytes() as shown earlier. They are not drop-in compatible — even argument names like system_instruction versus config.system_instruction shift between the two.

For a side-by-side migration, the google-genai Python SDK Complete Migration Guide catalogs every changed call.

Logging your way to the truth

If the three fixes above do not resolve the issue, the next step is to verify with logs that the image actually made it into the request. Inspecting the contents array — confirming a Part(inline_data=...) is in there — is a quick way to narrow things down.

from google import genai
from google.genai import types
from pathlib import Path
 
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
image_bytes = Path("product.jpg").read_bytes()
image_part = types.Part.from_bytes(data=image_bytes, mime_type="image/jpeg")
 
contents = ["Describe this image in under 50 characters.", image_part]
 
# Debug log: print each part type
for i, c in enumerate(contents):
    if isinstance(c, str):
        print(f"[{i}] text: {c[:30]}...")
    elif isinstance(c, types.Part):
        if c.inline_data:
            print(f"[{i}] inline_data: mime={c.inline_data.mime_type}, "
                  f"bytes={len(c.inline_data.data)}")
        else:
            print(f"[{i}] part: {c}")
 
response = client.models.generate_content(model="gemini-2.5-flash", contents=contents)
print("---")
print(response.text)
 
# Expected output:
# [0] text: Describe this image in under 50...
# [1] inline_data: mime=image/jpeg, bytes=384921
# ---
# Brown leather shoulder bag with gold hardware.

If the log clearly shows inline_data: mime=image/jpeg, bytes=... and the model still claims it cannot see the image, the most likely culprits are a corrupted or blank image and the Safety Filter rejecting it. To check the latter, inspect response.prompt_feedback and look for block_reason. A block_reason of SAFETY means the image triggered a content policy and was effectively discarded before reasoning, which Gemini communicates by saying it sees no usable image.

When multiple images are involved

A subtler version of the bug appears when only some of your images are recognized. This typically happens when one of the images is corrupted, oversized, or has an unsupported encoding, but the others are fine. Gemini will quietly process the valid ones and act as if the bad one never existed — which is convenient most of the time, but confusing when you are debugging.

The defensive pattern is to validate each image up front before assembling the contents array.

from pathlib import Path
from google.genai import types
 
SUPPORTED = {"image/jpeg", "image/png", "image/webp", "image/heic", "image/heif"}
MAX_INLINE_BYTES = 19 * 1024 * 1024  # leave 1 MB headroom under the 20 MB limit
 
def to_part(path: str, mime_type: str) -> types.Part:
    if mime_type not in SUPPORTED:
        raise ValueError(f"unsupported mime_type {mime_type} for {path}")
    data = Path(path).read_bytes()
    if len(data) == 0:
        raise ValueError(f"{path} is empty")
    if len(data) > MAX_INLINE_BYTES:
        raise ValueError(f"{path} is {len(data)} bytes, use Files API instead")
    return types.Part.from_bytes(data=data, mime_type=mime_type)
 
# Now pre-validate before calling the API
parts = [
    to_part("front.jpg", "image/jpeg"),
    to_part("back.png", "image/png"),
]
contents = ["Compare the two product photos and summarize differences."] + parts

This validator catches the most common silent failures before the request even leaves your machine. When you do hit one of these errors, the stack trace points at a specific image — far easier to act on than a vague "I cannot see your image" reply.

A quick note for Vertex AI users

If you have switched from the AI Studio API key flow to Vertex AI (typically because you wanted regional data residency or higher quotas), the exact same pitfalls apply but the client setup differs. With Vertex AI, the Client is initialized through Application Default Credentials and a project/location instead of an API key, but types.Part.from_bytes() and the Files API behave identically.

# Vertex AI version of the same fix
from google import genai
from google.genai import types
from pathlib import Path
 
client = genai.Client(
    vertexai=True,
    project="your-gcp-project-id",
    location="us-central1",
)
 
image_bytes = Path("product.jpg").read_bytes()
image_part = types.Part.from_bytes(data=image_bytes, mime_type="image/jpeg")
 
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=["Describe the product features.", image_part],
)
print(response.text)

The most common Vertex-specific surprise is that an API key that works against generativelanguage.googleapis.com will fail against aiplatform.googleapis.com because they are entirely separate authentication paths. If you see 401 Unauthenticated instead of "I cannot see the image," that is the symptom — check whether your code accidentally uses the wrong client mode.

A note for Node.js / TypeScript users

Everything above also applies to the @google/genai Node.js SDK, with two differences that bite frequently. First, in browser environments the Buffer global is not available, so when you read a file via FileReader or fetch the bytes from a URL you need to convert them to a Uint8Array yourself. Second, the property is camelCase: mimeType, not mime_type.

// ✅ Node.js / TypeScript correct usage
import { GoogleGenAI } from "@google/genai";
import { readFileSync } from "node:fs";
 
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
 
const imageBytes = readFileSync("product.jpg");
const response = await ai.models.generateContent({
  model: "gemini-2.5-flash",
  contents: [
    { text: "Describe the product features." },
    {
      inlineData: {
        mimeType: "image/jpeg",        // ← camelCase, not snake_case
        data: imageBytes.toString("base64"),  // base64 string, not Buffer
      },
    },
  ],
});
console.log(response.text);

The data field expects a base64-encoded string, not a Buffer or Uint8Array. Forgetting .toString("base64") is the Node-side equivalent of forgetting mime_type — the API silently treats the request as image-less.

Final checklist

When the issue persists, walk this checklist in order from cheapest to most expensive to verify:

  • Are you using a multimodal-capable model like gemini-2.5-flash or gemini-2.5-pro?
  • Did you pass Part.from_bytes(data=..., mime_type=...) with explicit mime_type?
  • Is the total payload under 20 MB? (If not, switch to the Files API.)
  • Is the file JPEG, PNG, WebP, HEIC, or HEIF? (Animated GIF and SVG are not supported.)
  • Are you on google-genai rather than the legacy google-generativeai?
  • Does prompt_feedback.block_reason show SAFETY?
  • Can Google AI Studio display the same image successfully?

In my experience, the first three items on this list resolve about nine out of ten "image not visible" reports. For a broader review of multimodal input patterns, Gemini API Multimodal: The Complete Guide is worth bookmarking, and the token cost optimization guide is helpful once you have the basics working and want to keep your bills predictable.

Write this checklist on a sticky note and keep it next to your editor — the next time the symptom appears you should be able to identify the root cause within three minutes. For today, try adding the missing mime_type line to whichever script has been frustrating you. More often than not, that one line is the difference between an invisible image and a useful response.

Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API / SDK2026-05-17
Diagnosing INVALID_ARGUMENT Errors in Gemini API Batch Image Analysis
When using the Gemini API to analyze multiple images at once, INVALID_ARGUMENT errors can be surprisingly hard to diagnose. This guide covers the three root causes—MIME type mismatches, inline data size limits, and contents structure errors—with code to fix each one.
API / SDK2026-06-28
Mixing Text and Images in One File Search Skewed My Results Toward Images — Rebalancing by Modality After Retrieval
When you put text and images in a single File Search store with gemini-embedding-2, results can quietly skew toward one modality. Here is how to measure that skew and even it out after retrieval, using per-modality normalization and quota-based merging — with working code.
API / SDK2026-06-21
Gemini API Implicit Caching Not Working — Troubleshooting Guide by Root Cause
Troubleshoot Gemini API implicit caching issues: cache not hitting, unexpectedly high costs, or low cache hit rates. Covers token thresholds, prompt structure, model version consistency, TTL expiry, and multimodal caching with code examples.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →