Skip to content

Conversation

@jesse-spevack
Copy link

@jesse-spevack jesse-spevack commented Dec 1, 2025

Summary

Fixes TypeError: Expected Array or String, got Hash when using VertexAI provider on Google Compute Engine with default credentials.

Problem

Google::Auth.get_application_default expects scope as a positional argument, not a keyword argument. The current code passes:

Google::Auth.get_application_default(
  scope: [...]  # keyword argument
)

Ruby interprets scope: [...] as passing a Hash {scope: [...]} to the first positional parameter. The signet gem then raises:

TypeError: Expected Array or String, got Hash

This only manifests when running on GCE with default credentials (not with service account key files, where authentication works differently).

How We Found This

We were running a Rails app on a GCE VM using RubyLLM 1.9.1 with the VertexAI provider. Episode processing jobs that called RubyLLM.chat(model: "gemini-2.0-flash", provider: :vertexai).ask(prompt) were failing with:

/gems/signet-0.21.0/lib/signet/oauth_2/client.rb:420:in 'Signet::OAuth2::Client#scope=': Expected Array or String, got Hash (TypeError)
    from /gems/signet-0.21.0/lib/signet/oauth_2/client.rb:193:in 'Signet::OAuth2::Client#update!'
    from /gems/googleauth-1.15.1/lib/googleauth/signet.rb:55:in 'Signet::OAuth2::Client#update!'
    from /gems/googleauth-1.15.1/lib/googleauth/compute_engine.rb:168:in 'Google::Auth::GCECredentials#update!'
    from /gems/signet-0.21.0/lib/signet/oauth_2/client.rb:115:in 'Signet::OAuth2::Client#initialize'
    from /gems/googleauth-1.15.1/lib/googleauth/compute_engine.rb:94:in 'Google::Auth::GCECredentials#initialize'
    from /gems/googleauth-1.15.1/lib/googleauth/application_default.rb:61:in 'Google::Auth.get_application_default'
    from /gems/ruby_llm-1.9.1/lib/ruby_llm/providers/vertexai.rb:43:in 'RubyLLM::Providers::VertexAI#initialize_authorizer'

We traced the error to signet-0.21.0/lib/signet/oauth_2/client.rb:

def scope=(new_scope)
  case new_scope
  when Array
    @scope = new_scope
  when String
    @scope = new_scope.split
  when nil
    @scope = nil
  else
    raise TypeError, "Expected Array or String, got #{new_scope.class}"
  end
end

The new_scope was a Hash {scope: [...]} instead of an Array.

Minimal Reproduction

On a GCE VM with the cloud-platform OAuth scope:

require 'ruby_llm'

RubyLLM.configure do |config|
  config.vertexai_project_id = 'your-project'
  config.vertexai_location = 'us-central1'
end

# This raises TypeError: Expected Array or String, got Hash
RubyLLM.chat(model: 'gemini-2.0-flash', provider: :vertexai).ask('hello')

To verify the root cause:

require 'googleauth'

# This works (positional argument):
Google::Auth.get_application_default(['https://www.googleapis.com/auth/cloud-platform'])

# This fails (keyword argument interpreted as Hash):
Google::Auth.get_application_default(scope: ['https://www.googleapis.com/auth/cloud-platform'])
# => TypeError: Expected Array or String, got Hash

Solution

Change from keyword to positional argument:

# Before (broken)
@authorizer = ::Google::Auth.get_application_default(
  scope: [
    'https://www.googleapis.com/auth/cloud-platform',
    'https://www.googleapis.com/auth/generative-language.retriever'
  ]
)

# After (fixed)
@authorizer = ::Google::Auth.get_application_default(
  [
    'https://www.googleapis.com/auth/cloud-platform',
    'https://www.googleapis.com/auth/generative-language.retriever'
  ]
)

Testing

  • Added unit test that verifies get_application_default is called with an Array, not a Hash
  • Verified fix works in production on GCE VM with cloud-platform OAuth scope

Environment

  • ruby_llm 1.9.1
  • googleauth 1.15.1
  • signet 0.21.0
  • Ruby 3.4.5
  • Google Compute Engine VM

Google::Auth.get_application_default expects scope as a positional
argument, not a keyword argument. Passing `scope: [...]` causes Ruby
to interpret it as a Hash, which triggers a TypeError in the signet
gem when running on GCE:

  TypeError: Expected Array or String, got Hash

This fix changes from keyword to positional argument syntax.
jesse-spevack added a commit to jesse-spevack/tts that referenced this pull request Dec 1, 2025
Documents the bug we found, our workaround, and the PR submitted
to fix it upstream: crmne/ruby_llm#520
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant