<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Robert Moore</title>
    <description>The latest articles on DEV Community by Robert Moore (@iamrobertmoore).</description>
    <link>https://dev.to/iamrobertmoore</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4076110%2F4e95763a-b90c-4749-8902-67794ea0f4e8.jpg</url>
      <title>DEV Community: Robert Moore</title>
      <link>https://dev.to/iamrobertmoore</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iamrobertmoore"/>
    <language>en</language>
    <item>
      <title>Five things I found in Google's Agent Registry and Agent Gateway that aren't in the docs</title>
      <dc:creator>Robert Moore</dc:creator>
      <pubDate>Thu, 13 Aug 2026 10:33:34 +0000</pubDate>
      <link>https://dev.to/iamrobertmoore/five-things-i-found-in-googles-agent-registry-and-agent-gateway-that-arent-in-the-docs-236f</link>
      <guid>https://dev.to/iamrobertmoore/five-things-i-found-in-googles-agent-registry-and-agent-gateway-that-arent-in-the-docs-236f</guid>
      <description>&lt;p&gt;I spent a couple of weeks building on Google's new Gemini Enterprise Agent Platform, and hit five things that cost me real time because none of them are documented. Writing them down so the next person can search for the error message and find something.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I built this for the All Things Agentic Hackathon. The project is &lt;a href="https://github.com/iamrobertmoore/agent-attest" rel="noopener noreferrer"&gt;agent-attest&lt;/a&gt;, if you want the context.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Agent Registry silently requires interface URLs to be globally unique
&lt;/h2&gt;

&lt;p&gt;This one cost me the most.&lt;/p&gt;

&lt;p&gt;I had four agents sharing one container image, which meant they all defaulted to the same port. Registering the first one worked. The other three vanished.&lt;/p&gt;

&lt;p&gt;Not errored. Vanished. &lt;code&gt;services.create&lt;/code&gt; returned HTTP 200 with an operation name, my code treated that as success, and moved on. The Firestore records I wrote alongside said those agents were registered. The registry said otherwise.&lt;/p&gt;

&lt;p&gt;The reason only turned up when I went looking at the operations list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The request was invalid: generic::invalid_argument:
            Interface URL 'http://localhost:8080' is already in use by another service."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things make this expensive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;services.create&lt;/code&gt; returns a &lt;strong&gt;long-running operation&lt;/strong&gt;, and the uniqueness check happens asynchronously inside it. If you take the 200 as success you never see the failure.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;first&lt;/strong&gt; registration always works. So it looks like a race condition, not a constraint on a field nobody thinks of as a key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reference for &lt;code&gt;Service.interfaces[]&lt;/code&gt; says nothing about uniqueness. If you're registering more than one agent, give each a distinct URL and poll the operation to &lt;code&gt;done&lt;/code&gt; rather than trusting the acknowledgement.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Agent Gateway doesn't support A2A
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;protocols&lt;/code&gt; field on an AgentGateway accepts exactly two values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;protocols&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;enum&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MCP&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PROTOCOL_UNSPECIFIED&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's no A2A.&lt;/p&gt;

&lt;p&gt;That matters if you're building the kind of thing the docs encourage. ADK's inter-agent transport is A2A: &lt;code&gt;RemoteA2aAgent&lt;/code&gt; on the calling side, &lt;code&gt;to_a2a()&lt;/code&gt; on the serving side, agent cards at &lt;code&gt;/.well-known/&lt;/code&gt;. So if you build a fleet where agents delegate to each other over the network, Agent Gateway can't sit in front of those calls. It handles MCP tool and server traffic only.&lt;/p&gt;

&lt;p&gt;I only found this because the import validator prints its entire JSON schema when you hand it a document it doesn't like, which turned out to be the single most useful error message on the whole platform. Worth remembering as a technique.&lt;/p&gt;

&lt;p&gt;While I was in there, three more things from that same schema dump that aren't in the prose docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;top-level &lt;code&gt;network&lt;/code&gt; and &lt;code&gt;subnetwork&lt;/code&gt; are rejected. &lt;code&gt;additionalProperties&lt;/code&gt; is false, and networking goes under &lt;code&gt;networkConfig&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;you have to pick &lt;code&gt;googleManaged&lt;/code&gt; or &lt;code&gt;selfManaged&lt;/code&gt;: a Google-run proxy in a tenant project, or attaching to an existing load balancer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;registries&lt;/code&gt; uses an unusual double-slash form, &lt;code&gt;//agentregistry.googleapis.com/projects/{p}/locations/{l}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Google's own Agent Gateway samples set failOpen: true
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;failOpen&lt;/code&gt; on an authorization extension defaults to &lt;code&gt;false&lt;/code&gt;, and the reference is clear about what that means: if the extension times out or fails, request processing stops and the client gets a 500.&lt;/p&gt;

&lt;p&gt;That's the right default for an authorization control. But every authz sample in the Agent Gateway docs sets it to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Copy one of those and your authorization check quietly becomes advisory. Anything that makes the callout unreachable also makes it irrelevant, and an attacker can arrange that. Set it to &lt;code&gt;false&lt;/code&gt; explicitly so nobody later assumes the sample was fine.&lt;/p&gt;

&lt;p&gt;Related, and worse: &lt;strong&gt;&lt;code&gt;CONTENT_AUTHZ&lt;/code&gt; can't be made fail-closed at all.&lt;/strong&gt; Under &lt;code&gt;FULL_DUPLEX_STREAMED&lt;/code&gt; the docs say the proxy fail-opens up to the first chunk of body data, and that connection and header failures fail open &lt;em&gt;regardless&lt;/em&gt; of what you set &lt;code&gt;failOpen&lt;/code&gt; to. If you need a genuine fail-closed guarantee, &lt;code&gt;REQUEST_AUTHZ&lt;/code&gt; is your only option.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. A googleManaged gateway is describable long before it's usable
&lt;/h2&gt;

&lt;p&gt;I created an Agent Gateway. It came back with a name, an etag and timestamps. &lt;code&gt;describe&lt;/code&gt; found it straight away. Everything looked fine.&lt;/p&gt;

&lt;p&gt;Then binding an authorization policy to it failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failed to update tenant configuration for AgentGateway:
generic::cancelled: agent gateway "agent-attest-gw" is not yet provisioned in the tenant project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;googleManaged&lt;/code&gt; gateway gets provisioned asynchronously in a Google-run tenant project. The resource exists in your project well before that finishes. There's no state field to poll, so the only signal you get is the error on whatever operation needs it.&lt;/p&gt;

&lt;p&gt;I waited. Four hours later it was still the same message.&lt;/p&gt;

&lt;p&gt;If you're scripting this, retry on that specific string and fail fast on anything else. Don't blanket-retry, or you'll turn an unrelated error into a timeout.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. A private Cloud Run service 404s, it doesn't 403
&lt;/h2&gt;

&lt;p&gt;Not Agent Platform specific, but it ate an afternoon so it's going in.&lt;/p&gt;

&lt;p&gt;Deploy a Cloud Run service with &lt;code&gt;--no-allow-unauthenticated&lt;/code&gt; and an unauthorised caller gets &lt;strong&gt;404 from the Google frontend&lt;/strong&gt;, not 403. That's deliberate, so private services don't leak their own existence. It also means "wrong URL" and "no access" are indistinguishable from outside.&lt;/p&gt;

&lt;p&gt;Now stack two more things on top:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gcloud run services describe --format='value(status.url)'&lt;/code&gt; can hand you a legacy hostname that doesn't route, while &lt;code&gt;gcloud run services list&lt;/code&gt; shows the one that does&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gcloud auth print-identity-token&lt;/code&gt; with no &lt;code&gt;--audiences&lt;/code&gt; mints a token audienced to gcloud's own OAuth client, which Cloud Run rejects. And &lt;code&gt;--audiences&lt;/code&gt; &lt;strong&gt;requires a service account&lt;/strong&gt;, so as a human you can't mint one it will accept at all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result: a service reporting &lt;code&gt;Ready: True&lt;/code&gt; and &lt;code&gt;RoutesReady: True&lt;/code&gt;, 404ing on every URL you try, that is completely healthy. I spent a long time debugging routing when the problem was authentication.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gcloud run services proxy&lt;/code&gt; handles the auth for you. But note it reads &lt;code&gt;status.url&lt;/code&gt; too, so if that field is stale the proxy fails as well, and a proxy failure tells you nothing about your container.&lt;/p&gt;




&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;Four of these five share a shape: &lt;strong&gt;something failed, and the thing reporting back said success or said nothing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An operation that returns 200 and fails later. A launcher that logs &lt;code&gt;Workload completed&lt;/code&gt; after refusing to run your workload (that's a Confidential Space one I left out). A gateway that reports created when it means queued. A 404 that means 403.&lt;/p&gt;

&lt;p&gt;The habit I ended up with was to stop trusting acknowledgements and check the thing itself. Poll the operation. Read the resource. And when a system gives you a genuinely detailed error, like that schema dump, treat it as the documentation, because sometimes it is.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written for the All Things Agentic Hackathon. Code is at &lt;a href="https://github.com/iamrobertmoore/agent-attest" rel="noopener noreferrer"&gt;github.com/iamrobertmoore/agent-attest&lt;/a&gt; if you want to see what these were getting in the way of.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>ai</category>
      <category>security</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
