Gemini-embedding-001 api issues

the documentation says “You can also generate embeddings for multiple chunks at once by passing them in as a list of strings.”

curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-001:embedContent" \
-H "x-goog-api-key: $GEMINI_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"model": "models/gemini-embedding-001",
     "content": [
        {"parts": [{"text": "What is the meaning of life?"}]},
        {"parts": [{"text": "What is the purpose of existence?"}]},
        {"parts": [{"text": "How do I bake a cake?"}]}
        ]
    }'

however the api returns this error

{
  "error": {
    "code": 400,
    "message": "Invalid JSON payload received. Unknown name \"content\": Proto field is not repeating, cannot start list.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "description": "Invalid JSON payload received. Unknown name \"content\": Proto field is not repeating, cannot start list."
          }
        ]
      }
    ]
  }
}

Hello,

Welcome to the Forum!

Thank you for highlighting this issue. We were able to reproduce it and it does appear to be a bug. In the meantime, you can refer to the command in the Gemini API reference.

@Lalit_Kumar is this alrady fixed?

I am also had a problem with gemini-embedding-001 batch file using .jsonl file upload through GCS.

here my problem

{
  "key": "id_1",
  "request": {
    "content": {
      "parts": [
        {
          "text": "dummy embedding text number 0"
        }
      ]
    },
    "embed_content_config": {
      "output_dimensionality": 768,
      "task_type": "RETRIEVAL_DOCUMENT",
      "title": "some_title_1"
    }
  },
  "status": "Bad Request: {\"error\": {\"code\": 400, \"message\": \"Invalid JSON payload received. Unknown name \\\"content\\\": Cannot find field.\\nInvalid JSON payload received. Unknown name \\\"embed_content_config\\\": Cannot find field.\", \"status\": \"INVALID_ARGUMENT\", \"details\": [{\"@type\": \"type.googleapis.com/google.rpc.BadRequest\", \"fieldViolations\": [{\"description\": \"Invalid JSON payload received. Unknown name \\\"content\\\": Cannot find field.\"}, {\"description\": \"Invalid JSON payload received. Unknown name \\\"embed_content_config\\\": Cannot find field.\"}]}]}}",
  "response": {},
  "processed_time": "2026-09-20T02:11:32.59+00:00"
}

I already copy and paste the exact payload example for gemini-embedding-001 : Get batch embeddings inferences  |  Gemini Enterprise Agent Platform  |  Google Cloud Documentation

and re-run the process, upload to GCS, run batch inference (I can see on google cloud console) then wait, then error, here my code

from __future__ import annotations

import os
import sys
import time
import asyncio
import argparse

from datetime import datetime
from pathlib import Path


sys.path.insert(0, str(Path(__file__).resolve().parents[3]))

from dotenv import load_dotenv


load_dotenv()  # populates os.environ for GOOGLE_APPLICATION_CREDENTIALS

from google import genai
from google.cloud import storage
from google.genai import types

from settings import settings # basically my settings, you can change the credential to reproduce


GEMINI_DIR = Path(__file__).resolve().parent
POLL_INTERVAL_S = 10

BATCH_INPUT_PATHS = {
    'gemini-embedding-001': GEMINI_DIR
    / 'batching_input_gemini_embedding_001.jsonl',
    'gemini-embedding-2': GEMINI_DIR
    / 'batching_input_gemini_embedding_2.jsonl',
}
# Input/output GCS paths are derived from the local input file's own
# name (not a separate hardcoded URI per model) so the local file, its
# GCS upload, and its output directory all share one name to track by.
GCS_BUCKET = 'gs://testing_aioverview'
_TERMINAL_STATES = {
    types.JobState.JOB_STATE_SUCCEEDED,
    types.JobState.JOB_STATE_FAILED,
    types.JobState.JOB_STATE_CANCELLED,
    types.JobState.JOB_STATE_PARTIALLY_SUCCEEDED,
    types.JobState.JOB_STATE_EXPIRED,
}


def _upload_to_gcs(path: Path, uri: str) -> None:
    storage_client = storage.Client(project=settings.GOOGLE_CLOUD_PROJECT)
    blob = storage.Blob.from_uri(uri, client=storage_client)
    blob.upload_from_filename(str(path))


def _count_rows(path: Path) -> int:
    with path.open(encoding='utf-8') as f:
        return sum(1 for line in f if line.strip())


async def _poll_and_report(
    batches_client,
    job,
    *,
    model: str,
    input_path: Path,
    row_count: int,
    start: datetime,
    start_monotonic: float,
) -> None:
    while job.state not in _TERMINAL_STATES:
        await asyncio.sleep(POLL_INTERVAL_S)
        job = await batches_client.get(name=job.name)
        print(f'  polled: state={job.state}')

    end_monotonic = time.monotonic()
    end = datetime.now()
    latency_ms = (end_monotonic - start_monotonic) * 1000

    print(f'model:        {model}')
    print(f'input file:   {input_path} ({row_count} row(s))')
    print(f'start time:   {start.isoformat()}')
    print(f'end time:     {end.isoformat()}')
    print(f'latency:      {latency_ms:.2f} ms')
    print(f'final state:  {job.state}')
    if job.error:
        print(f'job error:    {job.error}')
    if job.dest:
        print(f'output:       {job.dest}')


async def run_vertex_batch_job(model: str, location: str) -> None:
    input_path = BATCH_INPUT_PATHS[model]
    input_gcs_uri = f'{GCS_BUCKET}/{input_path.name}'
    output_gcs_uri = f'{GCS_BUCKET}/{input_path.stem}_output/'
    row_count = _count_rows(input_path)

    await asyncio.to_thread(_upload_to_gcs, input_path, input_gcs_uri)
    print(f'uploaded {input_path} ({row_count} row(s)) to {input_gcs_uri}')

    client = genai.Client(
        vertexai=True,
        project=settings.GOOGLE_CLOUD_PROJECT,
        location=location,
    )

    start = datetime.now()
    start_monotonic = time.monotonic()
    job = await client.aio.batches.create(
        model=model,
        src=input_gcs_uri,
        config=types.CreateBatchJobConfig(dest=output_gcs_uri),
    )
    print(f'submitted batch job: {job.name} (state={job.state})')
    await _poll_and_report(
        client.aio.batches,
        job,
        model=model,
        input_path=input_path,
        row_count=row_count,
        start=start,
        start_monotonic=start_monotonic,
    )


async def run_gemini_batch_job(model: str) -> None:
    api_key = os.environ.get('GEMINI_API_KEY')
    if not api_key:
        raise SystemExit(
            'GEMINI_API_KEY is not set -- add a Gemini Developer API key '
            'to .env to use --api gemini. Never hardcode it here.'
        )
    input_path = BATCH_INPUT_PATHS[model]
    row_count = _count_rows(input_path)

    client = genai.Client(vertexai=False, api_key=api_key)
    uploaded = await client.aio.files.upload(
        file=input_path,
        config=types.UploadFileConfig(
            display_name=input_path.stem, mime_type='jsonl'
        ),
    )
    print(f'uploaded {input_path} ({row_count} row(s)) as file: {uploaded.name}')

    start = datetime.now()
    start_monotonic = time.monotonic()
    job = await client.aio.batches.create_embeddings(
        model=model,
        src=types.EmbeddingsBatchJobSource(file_name=uploaded.name),
    )
    print(f'submitted batch job: {job.name} (state={job.state})')
    await _poll_and_report(
        client.aio.batches,
        job,
        model=model,
        input_path=input_path,
        row_count=row_count,
        start=start,
        start_monotonic=start_monotonic,
    )


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--model',
        required=True,
        choices=list(BATCH_INPUT_PATHS),
        help=(
            'Which embedding model to submit the batch job for -- picks '
            'the matching batching_input_gemini_embedding_*.jsonl file, '
            'which this script only uploads, never generates.'
        ),
    )
    parser.add_argument(
        '--api',
        default='vertex',
        choices=['vertex', 'gemini'],
        help=(
            "'vertex' (default): batches.create() via this project's "
            "normal Vertex credentials. 'gemini': batches.create_embeddings() "
            'via the Gemini Developer API instead (requires GEMINI_API_KEY '
            'in .env) -- confirmed live that Vertex rejects '
            'create_embeddings() outright (ValueError: ... does not '
            'support batches.create_embeddings), so this is the only way '
            'to use that method. --location is ignored for --api gemini.'
        ),
    )
    parser.add_argument(
        '--location',
        default=settings.GOOGLE_CLOUD_EMBEDDING_LOCATION,
        help=(
            'Vertex AI location for the batch job -- only used for '
            '--api vertex (default: settings.GOOGLE_CLOUD_EMBEDDING_LOCATION, '
            f'currently {settings.GOOGLE_CLOUD_EMBEDDING_LOCATION!r}).'
        ),
    )
    args = parser.parse_args()
    if args.api == 'vertex':
        asyncio.run(run_vertex_batch_job(args.model, args.location))
    else:
        asyncio.run(run_gemini_batch_job(args.model))


if __name__ == '__main__':
    main()

here my .jsonl file “batching_input_gemini_embedding_001.jsonl”,

{
  "key": "id_1",
  "request": {
    "content": {
      "parts": [
        {
          "text": "dummy embedding text number 0"
        }
      ]
    },
    "embed_content_config": {
      "output_dimensionality": 768,
      "title": "some_title_1",
      "task_type": "RETRIEVAL_DOCUMENT"
    }
  }
}

run using

uv run python assets/scripts/gemini/batching_embedding.py --model gemini-embedding-001 --api vertex --location asia-southeast1

Also I tried inside the cookbook, cookbook/quickstarts/Batch_mode.ipynb at main · google-gemini/cookbook · GitHub

here my .jsonl file “batching_input_gemini_embedding_001.jsonl”,

{
  "key": "request_1",
  "request": {
    "output_dimensionality": 3,
    "content": {
      "parts": [
        {
          "text": "1"
        }
      ]
    }
  }
}

it outputted

{
  "key": "request_1",
  "request": {
    "content": {
      "parts": [
        {
          "text": 1
        }
      ]
    },
    "output_dimensionality": 3
  },
  "status": "Bad Request: {\"error\": {\"code\": 400, \"message\": \"Invalid JSON payload received. Unknown name \\\"content\\\": Cannot find field.\\nInvalid JSON payload received. Unknown name \\\"output_dimensionality\\\": Cannot find field.\", \"status\": \"INVALID_ARGUMENT\", \"details\": [{\"@type\": \"type.googleapis.com/google.rpc.BadRequest\", \"fieldViolations\": [{\"description\": \"Invalid JSON payload received. Unknown name \\\"content\\\": Cannot find field.\"}, {\"description\": \"Invalid JSON payload received. Unknown name \\\"output_dimensionality\\\": Cannot find field.\"}]}]}}",
  "response": {},
  "processed_time": "2026-09-20T02:34:53.662+00:00"
}

I run on my machine using

  • python 3.12.11
  • google-genai 2.22.0

NOTE

  1. When I tried the payload for gemini-embedding-2 is work fine (just copy pasted what inside the documentation)

What happen?

Thank you