{"record":{"id":"a281ecb7af5bd12e","repo":"apache/beam","slug":"unable-to-obtain-credential-googleadsoptions","errorCode":null,"errorMessage":"Unable to obtain credential","messagePattern":"Unable to obtain credential","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"sdks/java/io/google-ads/src/main/java/org/apache/beam/sdk/io/googleads/GoogleAdsOptions.java","lineNumber":126,"sourceCode":"\n  /**\n   * Attempts to load the Google Ads credentials. See {@link CredentialFactory#getCredential()} for\n   * more details.\n   */\n  class GoogleAdsCredentialsFactory implements DefaultValueFactory<@Nullable Credentials> {\n    @Override\n    public @Nullable Credentials create(PipelineOptions options) {\n      GoogleAdsOptions googleAdsOptions = options.as(GoogleAdsOptions.class);\n      try {\n        CredentialFactory factory =\n            InstanceBuilder.ofType(CredentialFactory.class)\n                .fromClass(googleAdsOptions.getGoogleAdsCredentialFactoryClass())\n                .fromFactoryMethod(\"fromOptions\")\n                .withArg(PipelineOptions.class, options)\n                .build();\n        return factory.getCredential();\n      } catch (IOException | GeneralSecurityException e) {\n        throw new RuntimeException(\"Unable to obtain credential\", e);\n      }\n    }\n  }\n}\n","sourceCodeStart":108,"sourceCodeEnd":131,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/google-ads/src/main/java/org/apache/beam/sdk/io/googleads/GoogleAdsOptions.java#L108-L131","documentation":"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.","triggerScenarios":"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.","commonSituations":"Missing GOOGLE_APPLICATION_CREDENTIALS, expired or malformed service-account JSON, wrong scopes, network/clock issues during token exchange.","solutions":["Verify credentials are configured (googleApplicationCredentials / GOOGLE_APPLICATION_CREDENTIALS) and the JSON is valid and unexpired","Check the custom GoogleAdsCredentialFactoryClass implementation for errors in fromOptions","Inspect the cause chain (IOException/GeneralSecurityException) for the root reason","Confirm scopes and developer token are valid for the account"],"exampleFix":"// before\n--defaultWorkerHarnessContainerImage... options without credentials\n// after\noptions.setGcpCredential(SecretHelper.getCredential()); // or set GOOGLE_APPLICATION_CREDENTIALS=/path/sa.json","handlingStrategy":"try-catch","validationCode":"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\");","typeGuard":null,"tryCatchPattern":"try { GoogleAdsIO.read().from(...).expand(p); } catch (RuntimeException e) { if (\"Unable to obtain credential\".equals(e.getMessage())) { reconfigureCredentials(); } else throw e; }","preventionTips":["Set GOOGLE_APPLICATION_CREDENTIALS or options.setGcpCredential before launch","Validate the service-account JSON loads in a smoke test","Keep developer token and scopes current"],"tags":["java","apache-beam","google-ads","authentication"],"backgroundTag":"missing-credentials","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}