diff --git a/packages/opencode/src/cli/cmd/providers.ts b/packages/opencode/src/cli/cmd/providers.ts
index 560781ef0e4..7271ba4a754 100644
--- a/packages/opencode/src/cli/cmd/providers.ts
+++ b/packages/opencode/src/cli/cmd/providers.ts
@@ -460,6 +460,13 @@ export const ProvidersLoginCommand = effectCmd({
)
}
+ if (provider === "google-vertex") {
+ yield* Prompt.log.info(
+ "Note: this API key cannot be used with Anthropic models.\n" +
+ "Use `gcloud auth application-default login` or set `GOOGLE_APPLICATION_CREDENTIALS`.",
+ )
+ }
+
if (provider === "opencode") {
yield* Prompt.log.info("Create an api key at https://opencode.ai/auth")
}
diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
index 874afa05b4b..0a8beaccf0c 100644
--- a/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
+++ b/packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
@@ -384,6 +384,15 @@ function ApiMethod(props: ApiMethodProps) {
),
+ "google-vertex": (
+
+ Note: this API key cannot be used with Anthropic models.
+
+ Use gcloud auth application-default login or set
+ GOOGLE_APPLICATION_CREDENTIALS.
+
+
+ ),
}[props.providerID] ?? undefined
}
onConfirm={async (value) => {
diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts
index 29cc83a1eb8..bd590cf8c4e 100644
--- a/packages/opencode/src/session/message-v2.ts
+++ b/packages/opencode/src/session/message-v2.ts
@@ -1095,8 +1095,9 @@ export function latest(msgs: WithParts[]) {
export function fromError(
e: unknown,
- ctx: { providerID: ProviderID; aborted?: boolean },
+ ctx: { model: Provider.Model; aborted?: boolean } | { providerID: ProviderID; aborted?: boolean },
): NonNullable {
+ const providerID = "model" in ctx ? ctx.model.providerID : ctx.providerID
switch (true) {
case e instanceof DOMException && e.name === "AbortError":
return new AbortedError(
@@ -1110,11 +1111,23 @@ export function fromError(
case LoadAPIKeyError.isInstance(e):
return new AuthError(
{
- providerID: ctx.providerID,
+ providerID,
message: e.message,
},
{ cause: e },
).toObject()
+ case e instanceof Error &&
+ "model" in ctx &&
+ ctx.model.api.npm === "@ai-sdk/google-vertex/anthropic" &&
+ e.message.includes("Could not load the default credentials"):
+ return new AuthError(
+ {
+ providerID,
+ message:
+ "Anthropic models on Google Vertex require Google Cloud credentials. Use `gcloud auth application-default login` or set `GOOGLE_APPLICATION_CREDENTIALS`.",
+ },
+ { cause: e },
+ ).toObject()
case (e as SystemError)?.code === "ECONNRESET":
return new APIError(
{
@@ -1157,7 +1170,7 @@ export function fromError(
).toObject()
case APICallError.isInstance(e):
const parsed = ProviderError.parseAPICallError({
- providerID: ctx.providerID,
+ providerID,
error: e,
})
if (parsed.type === "context_overflow") {
diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts
index a287c3b0068..b8d3025f336 100644
--- a/packages/opencode/src/session/processor.ts
+++ b/packages/opencode/src/session/processor.ts
@@ -124,7 +124,7 @@ export const layer = Layer.effect(
const parse = (e: unknown) =>
MessageV2.fromError(e, {
- providerID: input.model.providerID,
+ model: input.model,
aborted,
})
diff --git a/packages/opencode/test/session/message-v2.test.ts b/packages/opencode/test/session/message-v2.test.ts
index 82bed0e9cc6..7eb74ebe155 100644
--- a/packages/opencode/test/session/message-v2.test.ts
+++ b/packages/opencode/test/session/message-v2.test.ts
@@ -1509,6 +1509,37 @@ describe("session.message-v2.fromError", () => {
})
})
+ test("explains ADC requirements when Vertex Anthropic credentials are missing", () => {
+ const vertexAnthropicModel = {
+ ...model,
+ providerID: ProviderID.googleVertex,
+ api: { ...model.api, npm: "@ai-sdk/google-vertex/anthropic" },
+ }
+ const result = MessageV2.fromError(
+ new Error(
+ "Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.",
+ ),
+ { model: vertexAnthropicModel },
+ )
+
+ expect(result).toStrictEqual({
+ name: "ProviderAuthError",
+ data: {
+ providerID: "google-vertex",
+ message:
+ "Anthropic models on Google Vertex require Google Cloud credentials. Use `gcloud auth application-default login` or set `GOOGLE_APPLICATION_CREDENTIALS`.",
+ },
+ })
+ })
+
+ test("does not present Anthropic ADC guidance for Gemini Vertex credentials errors", () => {
+ const result = MessageV2.fromError(new Error("Could not load the default credentials."), {
+ model: { ...model, providerID: ProviderID.googleVertex, api: { ...model.api, npm: "@ai-sdk/google-vertex" } },
+ })
+
+ expect(result.name).toBe("UnknownError")
+ })
+
test("serializes tagged errors with their message", () => {
const result = MessageV2.fromError(new Question.RejectedError(), { providerID })