react-google-adk: ADK ignores useAdkSubmitAuth replies, so a tool waiting on credentials never resumes

Author: okisdevCreated Sep 17, 2026Updated Sep 17, 2026
Labelsbugpkg/react-google-adk

problem

the documented auth flow (runtimes/google-adk/hooks.mdx:163, :200, :233) passes a request from useAdkAuthRequests() to submitAuth(req.toolCallId, credential). ADK ignores that reply for two independent reasons, so the tool that asked for credentials never runs again.

  • wrong id. ADK matches a credential reply against the synthetic adk_request_credential call it emitted, never against the tool call that asked for auth. the accumulator takes toolCallId from function_call_id (packages/react-google-adk/src/AdkEventAccumulator.ts:460), which is that original tool call id on adk-js. adk-python dumps AuthToolArguments with by_alias=True (src/google/adk/flows/llm_flows/functions.py:179), so its args are camelCase: function_call_id is missing, toolCallId falls back to the synthetic id, and authConfig is the whole { functionCallId, authConfig } args object.
  • wrong payload. useAdkSubmitAuth sends the bare AdkAuthCredential as the response (packages/react-google-adk/src/hooks.ts:88). adk-js reads only exchangedAuthCredential off it (dist/esm/auth/credential_response_binding.js:56), and adk-python validates it as an AuthConfig, which requires authScheme (src/google/adk/auth/auth_preprocessor.py:149, src/google/adk/auth/auth_tool.py:59).

reproduction

@google/adk 2.0.0, the version examples/with-google-adk resolves: an InMemoryRunner whose scripted BaseLlm calls one FunctionTool. the tool calls toolContext.requestCredential with an apiKey scheme on its first execution and returns data on its second. a second run sends one user functionResponse named adk_request_credential, varying its id and response:

WARN: [ADK] 2026-09-17T14:07:49.720Z Ignoring credential response 'adk-38fbef5e-9f1b-405e-a1fa-76babfa954ea': no matching request from this agent.
{"label":"useAdkSubmitAuth today (original id, bare credential)","replyId":"original","executions":1,"resumed":false}
WARN: [ADK] 2026-09-17T14:07:49.721Z Ignoring credential response 'adk-8ab601a2-6304-4bd4-bf63-cb0880cd7e8c': it carries no credential for the request this agent raised.
{"label":"synthetic id, bare credential","replyId":"synthetic","executions":1,"resumed":false}
WARN: [ADK] 2026-09-17T14:07:49.722Z Ignoring credential response 'adk-46a6c478-6f34-448e-bb12-2b1491ebc5f7': no matching request from this agent.
{"label":"original id, exchangedAuthCredential","replyId":"original","executions":1,"resumed":false}
{"label":"synthetic id, exchangedAuthCredential","replyId":"synthetic","executions":2,"resumed":true}

adk-python v2.9.1 applies the same two gates in _store_auth_and_collect_resume_targets (auth_preprocessor.py:122 matches the adk_request_credential call id, :149 validates the response); read at the tag, not run.

fix direction

expose the synthetic adk_request_credential call id as the request id, read auth_config or authConfig, and reply with the request's auth config carrying the credential as exchangedAuthCredential. both runtimes accept that shape: adk-js takes only exchangedAuthCredential and binds it to the request it issued, and adk-python validates the config, then keeps its own scheme and takes only the exchanged credential. useAdkSubmitAuth(toolCallId, credential) carries no auth config, so the helper has to find the pending request by id. the id change lands with #7425, whose pending list keys on the same id, and the three docs examples change with it.

Track in Rupic

Source: assistant-ui/assistant-ui