apache/beam · critical · RuntimeException

Unable to obtain credential

Error message

Unable to obtain credential

What it means

GoogleAdsOptions' credential factory wraps IOException/GeneralSecurityException from instantiating and invoking the configured GoogleAdsCredentialFactory in a RuntimeException 'Unable to obtain credential'. It means the OAuth credential for the Google Ads connector could not be created from pipeline options.

Solutions

  1. Verify credentials are configured (googleApplicationCredentials / GOOGLE_APPLICATION_CREDENTIALS) and the JSON is valid and unexpired
  2. Check the custom GoogleAdsCredentialFactoryClass implementation for errors in fromOptions
  3. Inspect the cause chain (IOException/GeneralSecurityException) for the root reason
  4. Confirm scopes and developer token are valid for the account

Example fix

// before
--defaultWorkerHarnessContainerImage... options without credentials
// after
options.setGcpCredential(SecretHelper.getCredential()); // or set GOOGLE_APPLICATION_CREDENTIALS=/path/sa.json
Defensive patterns

Strategy: try-catch

Validate before calling

Preconditions.checkNotNull(options.getGoogleAdsCredentialFactoryClass(), "credential factory not set"); Preconditions.checkNotNull(options.getGcpCredential() == null && System.getenv("GOOGLE_APPLICATION_CREDENTIALS") == null ? null : Boolean.TRUE, "no credentials configured");

Try / catch

try { GoogleAdsIO.read().from(...).expand(p); } catch (RuntimeException e) { if ("Unable to obtain credential".equals(e.getMessage())) { reconfigureCredentials(); } else throw e; }

Prevention

When it happens

Trigger: Calling the GoogleAdsIO read with options whose gcpCredential/clientSecrets cannot be loaded: missing/invalid service account key, bad secrets file path, or a factory's fromOptions throwing.

Common situations: Missing GOOGLE_APPLICATION_CREDENTIALS, expired or malformed service-account JSON, wrong scopes, network/clock issues during token exchange.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/a281ecb7af5bd12e. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/io/google-ads/src/main/java/org/apache/beam/sdk/io/googleads/GoogleAdsOptions.java:126

  /**
   * Attempts to load the Google Ads credentials. See {@link CredentialFactory#getCredential()} for
   * more details.
   */
  class GoogleAdsCredentialsFactory implements DefaultValueFactory<@Nullable Credentials> {
    @Override
    public @Nullable Credentials create(PipelineOptions options) {
      GoogleAdsOptions googleAdsOptions = options.as(GoogleAdsOptions.class);
      try {
        CredentialFactory factory =
            InstanceBuilder.ofType(CredentialFactory.class)
                .fromClass(googleAdsOptions.getGoogleAdsCredentialFactoryClass())
                .fromFactoryMethod("fromOptions")
                .withArg(PipelineOptions.class, options)
                .build();
        return factory.getCredential();
      } catch (IOException | GeneralSecurityException e) {
        throw new RuntimeException("Unable to obtain credential", e);
      }
    }
  }
}

View on GitHub (pinned to 12126d8942)