ANTIGRAVITY LABJP
Articles/Agents & Manager
Agents & Manager/2026-06-22Advanced

Your Antigravity Custom Tools Don't Break by Design — They Break on Re-execution: Field Notes on Idempotency and Error Contracts

Once you add a custom tool to an Antigravity agent, the real production problem is re-execution and duplicated side effects. Here are the idempotency keys, error contracts, health gates, and tool-sprawl checks that actually held up in practice.

antigravity386agents100tool-usereliability10idempotency9

Premium Article

An agent usually behaves beautifully right after you add a custom tool. The trouble shows up a few days later, when that same tool starts getting called more often than you expected. As an indie developer, I run a few automation agents across my own projects, and on one of them I added a single tool that talked to an internal API. The first week was flawless. The second week, I noticed the failure for the first time in the worst possible form: the same invoice had been created twice. The cause was not the tool's implementation. The agent had read a timeout as a failure and re-called an operation that had, in fact, already succeeded.

Articles on designing custom tools tend to stop at schemas and permission boundaries. But what actually matters in operation is resilience to re-execution — the property that nothing breaks when the same tool is called twice. These are the implementations and decision rules that earned their keep from that angle.

Where duplicate side effects come from

There are more paths to a double call than you'd think. A few recur.

One: the tool actually succeeds, but the response times out, and the agent decides it failed and retries. Two: midway through a long task, context gets compacted, the agent forgets a call it already made, and reconstructs it. Three: in a parallel-agent setup, two workers pick up the same task.

What they share is how they look from the tool's side — two near-identical calls arriving within a short window. That means you can mount your defense on the argument side.

Idempotency keys — never run the same operation twice

For write and destructive tools, make an idempotency key mandatory. The key is a string that names the unit "this operation should run exactly once." The caller (the agent) generates it; the tool stores it. When a second call arrives with the same key, the tool returns the first result instead of re-executing.

import time
 
class IdempotencyStore:
    """Holds idempotency keys and first results with a TTL.
    In production, back this with a shared store like Redis so parallel workers share it."""
    def __init__(self, ttl_seconds: int = 86400):
        self._store: dict[str, tuple[float, dict]] = {}
        self._ttl = ttl_seconds
 
    def get(self, key: str) -> dict | None:
        entry = self._store.get(key)
        if not entry:
            return None
        ts, result = entry
        if time.time() - ts > self._ttl:
            self._store.pop(key, None)
            return None
        return result
 
    def put(self, key: str, result: dict) -> None:
        self._store[key] = (time.time(), result)
 
 
def create_invoice(args: dict, idem: IdempotencyStore) -> dict:
    key = args.get("idempotency_key")
    if not key:
        return {"ok": False, "error": {"code": "MISSING_IDEMPOTENCY_KEY",
                "message": "Write operations require an idempotency_key.", "retryable": False}}
    cached = idem.get(key)
    if cached is not None:
        # On the second call onward, cause no side effect; return the first result
        return {**cached, "replayed": True}
    result = {"ok": True, "data": _do_create_invoice(args)}
    idem.put(key, result)
    return result

The point is that the caller generates the key. You could derive a key from a hash of the arguments on the tool side, but then you can't distinguish a legitimate "I deliberately want to create this twice" from an accidental re-execution. Make it a contract that the agent issues a unique key per operation and reuses the same key when retrying, and the two cases separate cleanly.

In the schema, make idempotency_key a required argument on write tools, and put the contract in the description: this operation is once-only; reuse the same key to retry.

CREATE_INVOICE_SCHEMA = {
    "name": "create_invoice",
    "description": "Creates an invoice. Idempotent. Reuse the same idempotency_key when retrying.",
    "parameters": {
        "type": "object",
        "properties": {
            "customer_id": {"type": "string"},
            "amount_cents": {"type": "integer", "minimum": 1},
            "idempotency_key": {
                "type": "string",
                "description": "A string uniquely identifying this invoice creation. Reuse the same value on retry.",
            },
        },
        "required": ["customer_id", "amount_cents", "idempotency_key"],
    },
}

Set the TTL by the nature of the operation. For things where "doing the same operation again tomorrow is almost certainly an accident" — invoices, payments — go 24 hours or more. For something like a notification, where you only need to block short-window duplicates, a few minutes is plenty. I give the destructive ones the longest windows.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
An idempotency-key design that keeps re-execution from doubling side effects (who generates it, where it's stored, TTL)
An error contract the agent can branch on — a schema with retryable and a grace period
A health gate so the agent stops hammering a downed dependency, plus a rule that curbs tool sprawl
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity 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 →

Related Articles

Agents & Manager2026-06-17
Making Managed Agent Batches Safe to Re-run: Idempotency and Checkpoints
Running overnight batches on the Antigravity 2.0 Managed Agents API makes recovery from partial failure unavoidable. Starting from a duplicate-post incident, I share the implementation of idempotency keys, a checkpoint store, and resume logic, with real numbers from solo operations.
Agents & Manager2026-06-13
When a Scheduled Agent Runs Twice — Designing for Idempotency Against Overlap and Retry
A scheduled agent can do the same work twice when the next run triggers before the last one finishes. Here is a design with an overlap lock and an idempotency guard that survives mid-run failures, drawn from a double-publish incident I ran into in production.
Agents & Manager2026-06-02
Rehearsing an Agent's Actions Before They Touch Production — Designing a Zero-Side-Effect Dry-Run Layer
Some accidents survive shadow mode and canaries: the very first time an agent touches an external API. This is the design and TypeScript implementation of a zero-side-effect dry-run layer you can bolt onto Antigravity's parallel agents, with the real numbers from running six sites autonomously.
📚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 →