{"record":{"id":"a75267d565057da8","repo":"apache/beam","slug":"copying-blobs-requires-that-a-sas-token-connection-string-or","errorCode":null,"errorMessage":"Copying blobs requires that a SAS token, connection string, or account key be provided.","messagePattern":"Copying blobs requires that a SAS token, connection string, or account key be provided\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"sdks/java/io/azure/src/main/java/org/apache/beam/sdk/io/azure/blobstore/AzureBlobStoreFileSystem.java","lineNumber":382,"sourceCode":"\n    sharedAccessAccountPolicy.setPermissionsFromString(DEFAULT_PERMISSIONS);\n    sharedAccessAccountPolicy.setSharedAccessStartTime(new Date(date));\n    sharedAccessAccountPolicy.setSharedAccessExpiryTime(new Date(expiryDate));\n    sharedAccessAccountPolicy.setResourceTypeFromString(DEFAULT_RESOURCE_TYPES);\n    sharedAccessAccountPolicy.setServiceFromString(DEFAULT_SERVICES);\n\n    String storageConnectionString;\n    if (!Strings.isNullOrEmpty(options.getAzureConnectionString())) {\n      storageConnectionString = options.getAzureConnectionString();\n    } else if (!Strings.isNullOrEmpty(options.getAccessKey())) {\n      storageConnectionString =\n          \"DefaultEndpointsProtocol=https;AccountName=\"\n              + client.get().getAccountName()\n              + \";AccountKey=\"\n              + options.getAccessKey()\n              + \";EndpointSuffix=core.windows.net\";\n    } else {\n      throw new IOException(\n          \"Copying blobs requires that a SAS token, connection string, or account key be provided.\");\n    }\n\n    try {\n      CloudStorageAccount storageAccount = CloudStorageAccount.parse(storageConnectionString);\n      return \"?\" + storageAccount.generateSharedAccessSignature(sharedAccessAccountPolicy);\n    } catch (Exception e) {\n      throw (IOException) e.getCause();\n    }\n  }\n\n  @Override\n  protected void rename(\n      List<AzfsResourceId> srcResourceIds,\n      List<AzfsResourceId> destResourceIds,\n      MoveOptions... moveOptions)\n      throws IOException {\n    if (moveOptions.length > 0) {","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/io/azure/src/main/java/org/apache/beam/sdk/io/azure/blobstore/AzureBlobStoreFileSystem.java#L364-L400","documentation":"generateSasToken() builds a legacy CloudStorageAccount SAS signature that requires one of: a SAS token, a connection string, or an account access key. When none of these credentials are configured in AzureOptions (e.g. only an OAuth access token is set), it throws this IOException. Server-side blob copy via SAS needs a writable credential to sign the shared access policy.","triggerScenarios":"Calling FileSystems.copy()/rename() on azfs paths while AzureOptions has only setAzureAccessToken() (or no credential at all) instead of setAccessKey(), setSasToken(), or a connection string.","commonSituations":"Pipelines authenticated with Azure AD/OAuth tokens (common on Dataflow) attempting blob rename/move; users unaware that copy requires account-key-class credentials rather than bearer tokens; credentials omitted entirely in the worker environment.","solutions":["Provide the account key: options.setAccessKey(\"...\") (or via --azureAccessKey) before running copy/rename.","Provide a SAS token with read/write/delete permissions via setSasToken().","Provide a connection string if your setup uses one.","If you must stay on OAuth tokens, implement copy manually (download then upload) instead of relying on SAS-based copy."],"exampleFix":"// before\nAzureOptions options = PipelineOptionsFactory.as(AzureOptions.class);\noptions.setAzureAccessToken(oauthToken); // copy() will throw\n// after\nAzureOptions options = PipelineOptionsFactory.as(AzureOptions.class);\noptions.setAccessKey(accountKey); // enables SAS generation for copy/rename","handlingStrategy":"validation","validationCode":"AzureOptions options = ...;\nif (options.getAccessKey() == null && options.getSasToken() == null /* and no connection string */) {\n  throw new IllegalStateException(\"copy/rename on azfs requires accessKey or sasToken\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  FileSystems.rename(srcIds, dstIds);\n} catch (IOException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"SAS token\")) {\n    // reconfigure credentials (accessKey/sasToken) before retrying\n  } else { throw e; }\n}","preventionTips":["Set AzureOptions.setAccessKey() or setSasToken() whenever the pipeline uses copy/rename on azfs paths.","Remember OAuth access tokens are not sufficient for SAS-based blob copy.","Provision credentials via pipeline options/secret manager, not hard-coded values."],"tags":["azure","authentication","sas-token","credentials","copy"],"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"}