{"record":{"id":"1db00013f155d1ba","repo":"apache/hadoop","slug":"sastoken-received-is-empty-or-null","errorCode":null,"errorMessage":"SASToken received is empty or null","messagePattern":"SASToken received is empty or null","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsClient.java","lineNumber":1166,"sourceCode":"   * @param queryBuilder to which SAS token is appended.\n   * @param cachedSasToken - previously acquired SAS token to be reused.\n   * @return sasToken - returned for optional re-use.\n   * @throws SASTokenProviderException if SAS token cannot be acquired.\n   */\n  protected String appendSASTokenToQuery(String path,\n                                         String operation,\n                                         AbfsUriQueryBuilder queryBuilder,\n                                         String cachedSasToken)\n      throws SASTokenProviderException {\n    String sasToken = null;\n    if (getAbfsConfiguration().validateForSASType(this.authType)) {\n      try {\n        LOG.trace(\"Fetch SAS token for {} on {}\", operation, path);\n        if (cachedSasToken == null) {\n          sasToken = sasTokenProvider.getSASToken(this.accountName,\n              this.filesystem, path, operation);\n          if ((sasToken == null) || sasToken.isEmpty()) {\n            throw new UnsupportedOperationException(\"SASToken received is empty or null\");\n          }\n        } else {\n          sasToken = cachedSasToken;\n          LOG.trace(\"Using cached SAS token.\");\n        }\n\n        // if SAS Token contains a prefix of ?, it should be removed\n        if (sasToken.charAt(0) == '?') {\n          sasToken = sasToken.substring(1);\n        }\n\n        queryBuilder.setSASToken(sasToken);\n        LOG.trace(\"SAS token fetch complete for {} on {}\", operation, path);\n      } catch (Exception ex) {\n        throw new SASTokenProviderException(String.format(\n            \"Failed to acquire a SAS token for %s on %s due to %s\", operation, path,\n            ex.toString()), ex);\n      }","sourceCodeStart":1148,"sourceCodeEnd":1184,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/AbfsClient.java#L1148-L1184","documentation":"The configured SASTokenProvider returned null or an empty string for the requested path/operation. With authType validated for SAS (e.g. SAS auth selected in fs.azure.account.auth.type), a usable token is mandatory, so the client throws UnsupportedOperationException (which the enclosing catch then wraps into SASTokenProviderException).","triggerScenarios":"A custom SASTokenProvider implementation whose getSASToken() returns null/empty for some operation type or path — commonly an unimplemented operation branch, an expired/missing secret read that returns null instead of throwing, or scope filters that skip the requested container.","commonSituations":"Custom KeyVault-backed providers returning null when a secret is disabled; providers written for read operations only, returning null for writes (like the delete-container operation); test providers stubbed with null.","solutions":["Fix the SASTokenProvider implementation to always return a non-empty, valid SAS token for every operation it is asked for, throwing explicitly when it genuinely cannot.","Unit-test the provider directly for all operation types (read, write, delete containers) used by the workload.","Verify the backing secret store entry exists, is enabled, and is readable by the provider's credentials."],"exampleFix":"// before (custom provider)\npublic String getSASToken(String account, String fs, String path, String op) {\n  return cache.get(op); // null on miss\n}\n// after\npublic String getSASToken(String account, String fs, String path, String op) {\n  String t = cache.computeIfAbsent(op, o -> keyVault.fetch(account, fs, o));\n  if (t == null || t.isEmpty()) {\n    throw new SASTokenProviderException(\"No SAS token available for \" + o);\n  }\n  return t;\n}","handlingStrategy":"validation","validationCode":"String t = provider.getSASToken(account, filesystem, path, op);\nif (t == null || t.isEmpty()) {\n  throw new IllegalStateException(\"SAS provider returned no token for \" + op);\n}","typeGuard":null,"tryCatchPattern":"catch (SASTokenProviderException ex) {\n  if (ex.getCause() instanceof UnsupportedOperationException\n      && ex.getCause().getMessage().contains(\"empty or null\")) {\n    // provider returned no token: fix the provider implementation\n  } else throw ex;\n}","preventionTips":["Unit-test custom SAS providers for every operation type the workload uses, asserting non-empty tokens.","Make providers throw explicit, descriptive errors instead of returning null.","Monitor secret-store health feeding the provider."],"tags":["azure","abfs","sas","auth","token-provider"],"backgroundTag":"sas-token-empty","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}