vercel/ai · error
OAuth client information must be saveable for dynamic regist
Error message
OAuth client information must be saveable for dynamic registration
What it means
During the OAuth authorization-code flow, the MCP client found no stored client registration and needs to dynamically register a new client with the authorization server. Dynamic registration only works if the provider supplies a saveClientInformation callback so the freshly registered client id/secret can be persisted for the subsequent token exchange. Without it, the flow cannot continue safely, so auth() throws.
Source
Thrown at packages/mcp/src/tool/oauth.ts:1324
clientInformation,
});
if (storedAuthorizationServerInformation) {
assertAuthorizationServerInformationMatches({
storedAuthorizationServerInformation,
currentAuthorizationServerInformation,
});
}
}
if (!clientInformation) {
if (authorizationCode !== undefined) {
throw new Error(
'Existing OAuth client information is required when exchanging an authorization code',
);
}
if (!provider.saveClientInformation) {
throw new Error(
'OAuth client information must be saveable for dynamic registration',
);
}
const fullInformation = await registerClient(authorizationServerUrl, {
metadata,
clientMetadata: {
...clientMetadata,
scope: selectedScope,
},
fetchFn,
});
clientInformation = addAuthorizationServerInformationToClientInformation(
fullInformation,
currentAuthorizationServerInformation,
);
await provider.saveClientInformation(clientInformation);View on GitHub (pinned to 69428b1f8b)
Solutions
- Implement saveClientInformation(clientInformation) on your OAuth provider object (e.g. persist to a config file, keychain, or database) so dynamic client registration can be saved.
- Pre-register a client with the authorization server and return it from provider.clientInformation() so the dynamic-registration path is never taken.
- If using a static client secret, configure provider.clientSecret / clientInformation so clientInformation() returns a value before authorization starts.
Example fix
// before
const provider = {
redirectUrl: 'http://localhost:3333/callback',
clientInformation: async () => undefined,
};
// after
const provider = {
redirectUrl: 'http://localhost:3333/callback',
clientInformation: async () => loadClientInfo(),
saveClientInformation: async (info) => {
await fs.writeFile('client-info.json', JSON.stringify(info));
},
}; Defensive patterns
Strategy: validation
Validate before calling
if (!provider.clientInformation && typeof provider.clientInformation === 'function') {
const existing = await provider.clientInformation();
if (!existing && typeof provider.saveClientInformation !== 'function') {
throw new Error('OAuth provider must implement saveClientInformation for dynamic registration');
}
} Type guard
function hasSaveClientInformation(p: object): p is { saveClientInformation: (info: unknown) => Promise<void> } {
return typeof (p as any).saveClientInformation === 'function';
} Try / catch
try {
await auth(serverUrl, { provider });
} catch (e) {
if (e instanceof Error && e.message.includes('must be saveable for dynamic registration')) {
// persist a client registration or supply provider.saveClientInformation
}
throw e;
} Prevention
- Always implement both clientInformation() and saveClientInformation() on custom OAuth providers
- Persist client registrations durably so restarts don't lose them
- Prefer pre-registered static clients when the AS supports it
- Add a startup assertion that the provider object satisfies the full OAuthProvider interface
When it happens
Trigger: Calling the MCP OAuth helper (auth() in packages/mcp/src/tool/oauth.ts) when: (1) provider.clientInformation() returns undefined (no previously registered client), and (2) the call carries an authorizationCode === undefined (start of the flow), and (3) provider.saveClientInformation is not implemented in the custom OAuthProvider passed in.
Common situations: Developers implementing a minimal custom OAuth provider for an MCP server forget to implement saveClientInformation, or pass an in-memory provider whose storage was cleared between runs, or use a provider built for a pre-registered/static client while the server actually requires dynamic registration.
Related errors
- Incompatible auth server: does not support dynamic client re
- OAuth authorization server metadata must be saveable before
- ACP MCP server name "ai-sdk-harness-tools" is reserved for H
- ACP MCP server name "ai-sdk-harness-tools" is reserved for H
- ACP MCP server ${JSON.stringify(name)} must be configured wi
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/405cd90f7c64f592.
Report an issue: GitHub.