Skip to content

· 10 min read · Nadeera Silva

AI & Automation

Google ADK 2.14 Upgrade Guide: Breaking Changes and Fixes

Google ADK for Python 2.14 changes Enum tool arguments, Vertex sessions and skill scripts. Here is what breaks, how to fix it and how to upgrade safely.

Illustration of a teal robot climbing a staircase of teal and amber blocks while an amber robotic arm places a glowing gear block, next to a glass sandbox cube and a checklist clipboard, representing a step-by-step Google ADK 2.14 upgrade

Google ADK for Python 2.14.0 shipped to PyPI on 7 November 2026, and it is a bigger upgrade than the version number suggests. The release moves execution environments, A2A and most of the evaluation stack out of experimental. It adds multi-run evals and Microsoft Teams support. It also includes three breaking changes that can quietly change how your tools behave. This guide walks through what changed in google adk 2.14, which parts of an existing agent need edits, and the order I would do the upgrade in. There is a runnable test so you can see the Enum change for yourself.

I checked every claim here on 8 November 2026 against the v2.14.0 release notes on GitHub, the google-adk 2.14.0 page on PyPI and the installed package itself.

What is Google ADK, briefly

ADK (Agent Development Kit) is Google's open-source framework for building, evaluating and deploying AI agents. The ADK docs list Python, TypeScript, Go, Java and Kotlin versions, and you install the Python one with pip install google-adk. It works with Gemini and, through adapters, with other model providers. This post only covers the Python package. If you are still choosing a framework, my Microsoft Agent Framework guide covers a comparable stack from the other side.

What changed in Google ADK 2.14

Here is the release in one table. The left column is what the notes call the change, and the right column is what it means for code you already have.

AreaWhat changed in 2.14.0Do you need to act?
Enum tool argumentsTools now receive Enum members, not raw stringsYes, if any tool takes an Enum
VertexAiSessionServicecreate_session only accepts display_name, ttl, expire_time and labels as extra kwargsYes, if you pass other keys
RunSkillScriptToolNo free-form command in environment modeYes, if prompts or evals send command
FirestoreSessionServiceNew storage format, no rollback to 2.13.0Back up first
DatabaseSessionServiceNew PostgreSQL columns are json, not jsonbOnly for outside queries on new tables
Environments and A2ANow stable, plus adk generate_agent_cardNo, but warnings go away
Evaluation--num_runs, --parallelism, ToolArgsMatcherOptional, but worth it
IntegrationsTeamsRunner, OpenAIDecisionsClassifier, MongoDB vector memoryOptional
Performance30-node, 200-turn workflow run 23% faster, 9% lower peak memoryNo

The performance figure comes from the release notes, not from my own benchmark.

Breaking change 1: Enum tool arguments arrive as members

This is the change most likely to break something without an error. If a FunctionTool parameter is typed as Enum, Optional[Enum] or list[Enum], ADK now converts what the model sent into Enum members before calling your function. In 2.13 your function got the raw string.

The release notes list what stops working for a plain Enum: comparing with arg == "value", string-keyed dict lookups, match on string literals, json.dumps, and string methods. The nasty part is the first one. Comparing a member to a string just returns False, so your tool takes the wrong branch and the agent carries on.

Before and after, as a test you can run

I ran this file with pytest against both versions. On 2.13.0, triage_old routes "high" to the urgent queue. On 2.14.0 the same call falls through to the normal queue, and all three tests below pass.

# test_enum_tool.py
# pip install google-adk==2.14.0 pytest
# python -m pytest -q test_enum_tool.py
import asyncio
from enum import Enum, StrEnum
from unittest.mock import MagicMock

from google.adk.tools import FunctionTool


class Priority(Enum):
    LOW = "low"
    HIGH = "high"


# Before: written for ADK 2.13, where the tool got the raw string "high"
def triage_old(priority: Priority) -> dict:
    if priority == "high":
        return {"queue": "urgent"}
    return {"queue": "normal"}


# After: compare against the member (or use priority.value)
def triage_new(priority: Priority) -> dict:
    if priority is Priority.HIGH:
        return {"queue": "urgent", "label": priority.value}
    return {"queue": "normal", "label": priority.value}


class Channel(StrEnum):
    EMAIL = "email"
    SMS = "sms"


def notify(channel: Channel) -> dict:
    # StrEnum members are real strings, so string code keeps working
    return {"sent_via": f"{channel}", "is_email": channel == "email"}


def call(fn, args):
    ctx = MagicMock()
    ctx.tool_confirmation = None
    ctx.function_call_id = None
    return asyncio.run(FunctionTool(fn).run_async(args=args, tool_context=ctx))


def test_old_string_compare_breaks_on_2_14():
    # The model sends "high", but the tool now receives Priority.HIGH
    assert call(triage_old, {"priority": "high"}) == {"queue": "normal"}


def test_member_compare_works():
    assert call(triage_new, {"priority": "high"}) == {"queue": "urgent", "label": "high"}


def test_strenum_keeps_string_behaviour():
    assert call(notify, {"channel": "email"}) == {"sent_via": "email", "is_email": True}

The MagicMock tool context is only there so the tool can run without a model or a session. In a real agent, ADK passes its own context.

How to fix your tools

You have three options, and the notes mention all of them:

  • Compare against members: priority is Priority.HIGH.
  • Use .value wherever you need the string, for example as a dict key or in JSON.
  • Switch the class to StrEnum (Python 3.11+), whose members behave as strings.

Two details from the notes: a callable require_confirmation still receives the raw values unless FUNCTION_TOOL_ARG_VALIDATION is on, and Enum members your tool returns are still sent to the model as their values.

Breaking change 2: VertexAiSessionService.create_session kwargs

If you use Vertex AI sessions, create_session now only accepts display_name, ttl, expire_time and labels as extra keyword arguments (camelCase forms are accepted too). Keys like http_options, session_state, sessionId and wait_for_completion now raise ValueError. Through adk api_server the same mistake comes back as HTTP 400.

# Before (raises ValueError on 2.14.0)
session = await session_service.create_session(
    app_name="support_app",
    user_id="u-123",
    sessionId="s-1",
    session_state={"plan": "pro"},
)

# After
session = await session_service.create_session(
    app_name="support_app",
    user_id="u-123",
    session_id="s-1",
    state={"plan": "pro"},
    display_name="Support chat",
)

Breaking change 3: RunSkillScriptTool no longer runs a shell command

When a SkillToolset runs in environment mode, the model used to be able to pass a free-form command to RunSkillScriptTool. In 2.14 it has to pass args, short_options and positional_args. Scripts run from the skill's own directory, and only .py, .sh and .bash files are allowed. Anything else returns UNSUPPORTED_SCRIPT_TYPE, and a call that still sends command returns INVALID_ARGUMENTS.

The release notes say plainly why: the old behaviour "let a prompt-injected model run arbitrary shell commands." That alone is a reason to upgrade if your agents load skills. If you are new to skills, my Agent Skills and SKILL.md guide explains how they are loaded.

Session storage changes to plan for

Two storage changes are listed as behaviour changes, not breaking changes, but I would treat the first one as the most important item in the release.

FirestoreSessionService: back up before you upgrade

FirestoreSessionService now stores event payloads as JSON strings and wraps nested lists in app and user state. Old sessions still load and accept new events. The catch is in the notes: sessions that 2.14 writes to "cannot be read by 2.13.0 or earlier after a rollback."

The same release also fixes a Firestore bug where list_sessions(user_id="") listed sessions for every user, and it now rejects session and user IDs that would break the document path.

DatabaseSessionService on PostgreSQL

New tables now get json columns instead of jsonb. Existing jsonb tables are not migrated and keep working. This only matters if something outside ADK queries new tables with jsonb operators or relies on GIN indexes.

New in Google ADK evaluation: adk eval --num_runs

Agents give different answers on different runs, so one passing eval can be luck. adk eval now accepts --num_runs to run each case several times and --parallelism to cap how many cases run at once. I checked both flags in adk eval --help on 2.14.0. If neither is set, each case runs once, and parallelism defaults to 4.

You can also put this in the eval config. This file validates against the 2.14.0 EvalConfig model:

{
  "criteria": {
    "tool_trajectory_avg_score": 1.0,
    "response_match_score": 0.6
  },
  "parallelism": 4,
  "multi_run_config": {
    "num_runs": 3,
    "cross_run_scoring_by_metric": {
      "response_match_score": "min_over_runs"
    }
  }
}
pip install "google-adk[eval]==2.14.0"
adk eval my_agent my_agent/evals/support.evalset.json \
  --config_file_path eval_config.json \
  --num_runs 3 --parallelism 2 --print_detailed_results

According to the eval config guide, the default averages every invocation across all runs, so a case can pass even if one run failed. min_over_runs makes the metric pass only if every run met the threshold. There is also a pass_rate rule. Each run starts from a fresh session, and every run is saved to the eval history.

Other eval additions: AgentEvaluator can take a BaseNode or App built at runtime, --eval_storage_uri stores eval files outside a read-only agents folder, and ToolArgsMatcher checks tool call arguments with exact, case-insensitive, numeric-tolerance or LLM-based semantic matching.

Other behaviour changes worth checking

  • /run_live now closes the socket with code 1007 on a malformed frame and 1000 when a run ends. Open a new connection for each live run.
  • Token-threshold compaction now counts tool outputs, so it triggers earlier with the same token_threshold.
  • The 16 features promoted to stable (including Bigtable, Computer Use, the Daytona and E2B environments, GCS, Pub/Sub and Spanner) can no longer be switched off with ADK_DISABLE_<FEATURE> or --disable_features, and you get no warning.
  • OpenAPI tool query parameters now follow their declared style and explode.
  • New teams and fsspec extras, and the e2b minimum is now 2.29.5.

Google ADK 2.14 upgrade checklist

Five-step Google ADK 2.14 upgrade path: back up sessions, pin 2.14.0 on a branch, fix three breaking changes, re-run evals with num_runs, check behaviour changes, with a Firestore rollback warning
The order I would follow when moving an agent from 2.13 to 2.14
  • Export Firestore session data, and note any outside jsonb queries on PostgreSQL.
  • Pin google-adk==2.14.0 on a branch and confirm you are on Python 3.11 or newer.
  • Search for Enum-typed tool parameters and fix string comparisons, dict keys and f-strings.
  • Search for create_session( calls on Vertex AI sessions and move state and IDs to state= and session_id=.
  • Remove command from skill prompts and eval cases.
  • Run your eval sets with --num_runs 3 before and after the upgrade and compare.
  • Reconnect per run on /run_live, and review compaction thresholds.

I use the same habit with every agent framework release now: read the breaking section first, then write one small test per change before touching production. It is the same approach I took for the Pydantic AI 2.66 security upgrade.

Frequently asked questions

What is Google ADK?

Google ADK (Agent Development Kit) is Google's open-source framework for building, evaluating and deploying AI agents. It is available for Python, TypeScript, Go, Java and Kotlin, and the Python package installs with pip install google-adk.

What is the latest version of Google ADK for Python?

As of 8 November 2026 the latest release is google-adk 2.14.0, published to PyPI and GitHub on 7 November 2026. There is no 2.14.x patch yet, and the package requires Python 3.11 or newer.

What are the breaking changes in Google ADK 2.14?

There are three. Enum-typed tool parameters now receive Enum members instead of strings, VertexAiSessionService.create_session only accepts display_name, ttl, expire_time and labels as extra keyword arguments, and RunSkillScriptTool no longer accepts a free-form shell command in environment mode.

Can I roll back from ADK 2.14 to 2.13?

Be careful if you use FirestoreSessionService. The release notes say sessions written by 2.14 cannot be read by 2.13.0 or earlier, so export your session data before upgrading.

How do I run an ADK eval case more than once?

Use adk eval with the --num_runs option, or set multi_run_config.num_runs in your eval config file. The --parallelism option limits how many cases run at the same time and defaults to 4.

Why does my ADK tool now compare an Enum to a string and get False?

Since 2.14.0, ADK converts the model's string into an Enum member before calling your tool, so a check like priority == "high" is False for a plain Enum. Compare against the member, use .value, or change the class to StrEnum.

Sources

  1. google/adk-python release v2.14.0 (GitHub (Google), 2026-11-07)
  2. google-adk 2.14.0 (PyPI, 2026-11-07)
  3. Agent Development Kit (ADK) documentation (Google)
  4. ADK eval config guide (GitHub (Google))
  5. Commit ff61524: RunSkillScriptTool validated paths and arguments (GitHub (Google))
  6. Commit 0894c03: Firestore event payload serialization (GitHub (Google))
  7. Commit 82fe029: Enum tool arguments as members (GitHub (Google))
SharePost on XLinkedIn

Comments

Share a question or note about this article.

Loading comments…

Google ADK 2.14 Upgrade: Enum Args, Sessions, adk eval | Nadeera Silva