Skip to content

storage: NewClient causes "multiple credential options provided" error when using WithCredentialsFile or WithCredentialsJSON #13531

@tbrownio

Description

@tbrownio

Description

When using storage.NewClient() with explicit credential options like option.WithCredentialsFile(), option.WithCredentialsJSON(), or option.WithAuthCredentialsFile(), the client fails with:

dialing: multiple credential options provided

This affects cloud.google.com/go/storage v1.55.0 and later.

Root Cause

The bug was introduced by two PRs merged into v1.55.0:

  1. PR fix(storage): migrate oauth2/google usages to cloud.google.com/go/auth #11191 (merged May 14, 2025) - Added internaloption.AuthCreds() call that parses user-provided credential options and appends option.WithAuthCredentials(creds)
  2. PR fix(storage): add EnableNewAuthLibrary internalOption to HTTP newClient #12320 (merged May 28, 2025) - Added internaloption.EnableNewAuthLibrary() which enables the new auth library validation

In storage.go NewClient() (lines 167-175):

// PR #12320 added this:
internaloption.EnableNewAuthLibrary(),

// PR #11191 added this:
c, err := internaloption.AuthCreds(ctx, opts)
if err == nil {
    creds = c
    opts = append(opts, option.WithAuthCredentials(creds))  // Duplicates credentials!
}

When a user passes any credential option:

  1. The credential option is already in opts
  2. AuthCreds() parses it into an auth.Credentials object
  3. WithAuthCredentials(creds) is appended - creating a second credential option
  4. Validation in google.golang.org/api/internal/settings.go:199-200 fails because nCreds > 1

Steps to Reproduce

package main

import (
    "context"
    "fmt"
    "cloud.google.com/go/storage"
    "google.golang.org/api/option"
)

func main() {
    ctx := context.Background()
    
    // Any of these will fail:
    _, err := storage.NewClient(ctx, option.WithCredentialsFile("/path/to/service-account.json"))
    // OR: _, err := storage.NewClient(ctx, option.WithCredentialsJSON([]byte(`{"type":"service_account",...}`)))
    // OR: _, err := storage.NewClient(ctx, option.WithAuthCredentialsFile(option.ServiceAccount, "/path/to/creds.json"))
    
    if err != nil {
        fmt.Printf("Error: %v\n", err)  // "dialing: multiple credential options provided"
    }
}

Expected Behavior

The storage client should accept explicit credential options without duplicating them internally. The AuthCreds() call should detect that credentials were already provided and skip appending WithAuthCredentials().

Workaround

Set GOOGLE_APPLICATION_CREDENTIALS environment variable instead of passing credential options:

os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/path/to/creds.json")
client, err := storage.NewClient(ctx)  // Uses ADC, works correctly

Environment

  • cloud.google.com/go/storage: v1.55.0+ (tested on v1.58.0)
  • google.golang.org/api: v0.258.0
  • Go version: 1.24.6
  • OS: Linux (Docker container)

Suggested Fix

Before appending WithAuthCredentials, check if the user already provided credentials:

c, err := internaloption.AuthCreds(ctx, opts)
if err == nil {
    creds = c
    // Only append if user didn't already provide credentials
    if !hasExplicitCredentials(opts) {
        opts = append(opts, option.WithAuthCredentials(creds))
    }
}

Or alternatively, don't append at all since AuthCreds already parsed the user's credentials and they're stored in the creds variable for later use.

Related Issues

Metadata

Metadata

Assignees

Labels

api: storageIssues related to the Cloud Storage API.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions