{"record":{"id":"353054a8ebe9760b","repo":"grpc/grpc-java","slug":"missing-or-empty-access-token-in-call-credentials","errorCode":null,"errorMessage":"Missing or empty access token in call credentials.","messagePattern":"Missing or empty access token in call credentials\\.","errorType":"validation","errorClass":"GrpcServiceParseException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/GrpcServiceConfigParser.java","lineNumber":259,"sourceCode":"\n  private static ConfiguredChannelCredentials extractChannelCredentials(\n      List<Any> channelCredentialPlugins) throws GrpcServiceParseException {\n    for (Any cred : channelCredentialPlugins) {\n      Optional<ConfiguredChannelCredentials> parsed = channelCredsFromProto(cred);\n      if (parsed.isPresent()) {\n        return parsed.get();\n      }\n    }\n    throw new GrpcServiceParseException(\"No valid supported channel_credentials found\");\n  }\n\n  private static Optional<CallCredentials> callCredsFromProto(Any cred)\n      throws GrpcServiceParseException {\n    if (cred.is(AccessTokenCredentials.class)) {\n      try {\n        AccessTokenCredentials accessToken = cred.unpack(AccessTokenCredentials.class);\n        if (accessToken.getToken().isEmpty()) {\n          throw new GrpcServiceParseException(\"Missing or empty access token in call credentials.\");\n        }\n        return Optional\n            .of(new SecurityAwareAccessTokenCredentials(MoreCallCredentials.from(OAuth2Credentials\n                .create(new AccessToken(accessToken.getToken(), new Date(Long.MAX_VALUE))))));\n      } catch (InvalidProtocolBufferException e) {\n        throw new GrpcServiceParseException(\n            \"Failed to parse access token credentials: \" + e.getMessage());\n      }\n    }\n    return Optional.empty();\n  }\n\n  private static Optional<CallCredentials> extractCallCredentials(List<Any> callCredentialPlugins)\n      throws GrpcServiceParseException {\n    List<CallCredentials> creds = new ArrayList<>();\n    for (Any cred : callCredentialPlugins) {\n      Optional<CallCredentials> parsed = callCredsFromProto(cred);\n      if (parsed.isPresent()) {","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/GrpcServiceConfigParser.java#L241-L277","documentation":"callCredsFromProto unpacks an AccessTokenCredentials plugin and requires a non-empty token string; when getToken() is empty it throws GrpcServiceParseException \"Missing or empty access token in call credentials.\" Otherwise it wraps the token in OAuth2Credentials/SecurityAwareAccessTokenCredentials for call auth.","triggerScenarios":"parsed encounters a call_credentials plugin of type AccessTokenCredentials whose token field is empty or absent; the empty-check runs right after cred.unpack(AccessTokenCredentials.class).","commonSituations":"Template/bootstrap generated with a placeholder access_token field never filled in; control plane delivered credentials before the token was minted; env var substitution failed leaving the token blank.","solutions":["Populate the access_token field in the AccessTokenCredentials plugin before channel construction","Fix the source of the token (env var, file, token agent) so a real value is injected into the config","Switch to per-RPC or workload-identity call credentials that fetch tokens dynamically instead of embedding a static token"],"exampleFix":"// before\n{\"access_token\": {\"token\": \"\"}}\n// after\n{\"access_token\": {\"token\": \"ya29.<real-token>\"}}\n","handlingStrategy":"validation","validationCode":"// Check the token is present before constructing the channel\nAccessTokenCredentials atc = cred.unpack(AccessTokenCredentials.class);\nif (atc == null || atc.getToken().isEmpty()) {\n  throw new IllegalArgumentException(\"AccessTokenCredentials.token must be non-empty\");\n}","typeGuard":"boolean hasNonEmptyToken(Any cred) throws InvalidProtocolBufferException {\n  return cred.is(AccessTokenCredentials.class)\n    && !cred.unpack(AccessTokenCredentials.class).getToken().isEmpty();\n}","tryCatchPattern":"try {\n  channel = ManagedChannelBuilder.forTarget(target)\n      .intercept(authInterceptor)\n      .build();\n} catch (GrpcServiceParseException e) {\n  if (e.getMessage().contains(\"access token\")) {\n    // refresh/mint the token and rebuild credentials\n  } else throw e;\n}","preventionTips":["Fail at config-load time if access_token is empty rather than at channel build","Verify token-injection (env substitution, secret manager mount) before deployment","Prefer dynamic CallCredentials (OAuth2Credentials with refresh) over static embedded tokens"],"tags":["xds","grpc-java","access-token","authentication"],"backgroundTag":"missing-credentials","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}