{"record":{"id":"d37f98d6c59939e8","repo":"apache/hadoop","slug":"gcs-path-must-include-non-empty-object-name-objec","errorCode":null,"errorMessage":"GCS path must include non-empty object name [objectName='%s', allowEmptyObjectName=%s]","messagePattern":"GCS path must include non-empty object name \\[objectName='(.+?)', allowEmptyObjectName=(.+?)\\]","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/StringPaths.java","lineNumber":87,"sourceCode":"\n  /**\n   * Validate the given object name to make sure that it can be used as a part of a file system\n   * path.\n   *\n   * <p>Note: this is not designed to duplicate the exact checks that GCS would perform on the\n   * server side. We make some checks that are relevant to using GCS as a file system.\n   *\n   * @param objectName           Object name to check.\n   * @param allowEmptyObjectName If true, a missing object name is not considered invalid.\n   */\n  static String validateObjectName(String objectName, boolean allowEmptyObjectName) {\n    LOG.trace(\"validateObjectName('{}', {})\", objectName, allowEmptyObjectName);\n\n    if (isNullOrEmpty(objectName) || objectName.equals(PATH_DELIMITER)) {\n      if (allowEmptyObjectName) {\n        objectName = \"\";\n      } else {\n        throw new IllegalArgumentException(String.format(\n            \"GCS path must include non-empty object name [objectName='%s',\"\n                + \" allowEmptyObjectName=%s]\", objectName, allowEmptyObjectName));\n      }\n    }\n\n    // We want objectName to look like a traditional file system path,\n    // therefore, disallow objectName with consecutive '/' chars.\n    for (int i = 0; i < (objectName.length() - 1); i++) {\n      if (objectName.charAt(i) == '/' && objectName.charAt(i + 1) == '/') {\n        throw new IllegalArgumentException(\n            String.format(\"GCS path must not have consecutive '/' characters: '%s'\", objectName));\n      }\n    }\n\n    // Remove leading '/' if it exists.\n    if (objectName.startsWith(PATH_DELIMITER)) {\n      objectName = objectName.substring(1);\n    }","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/StringPaths.java#L69-L105","documentation":"StringPaths.validateObjectName with allowEmptyObjectName=false requires a non-empty object name that is not just '/'. fromUriPath passes false whenever a concrete storage object is expected, so bucket-root URIs like gs://bucket or gs://bucket/ are rejected with IllegalArgumentException naming the objectName and the flag.","triggerScenarios":"fromUriPath(URI.create(\"gs://bucket\"), false) or 'gs://bucket/'; open/create on a bucket root; filenames computed as empty by path logic (e.g. substring after the last '/' when there is none).","commonSituations":"Treating the bucket root as a file path; empty filename from string operations or user input; directory-vs-file confusion where a directory URI (which legitimately has an empty object name) is fed to an API requiring an object.","solutions":["Append a concrete object name: gs://bucket/dir/file.txt.","Only pass allowEmptyObjectName=true where a directory/root path is acceptable (as listDirectory does).","Validate that the computed filename is non-empty before constructing the URI."],"exampleFix":"// before\nStorageResourceId.fromUriPath(URI.create(\"gs://bucket\"), false);\n// -> IllegalArgumentException: must include non-empty object name\n\n// after\nStorageResourceId.fromUriPath(URI.create(\"gs://bucket/file.txt\"), false);","handlingStrategy":"validation","validationCode":"String object = computedObjectName();\nif (object == null || object.isEmpty() || object.equals(\"/\")) {\n  throw new IllegalArgumentException(\"Object name required for file operations\");\n}\nURI uri = URI.create(\"gs://\" + bucket + \"/\" + object);\nStorageResourceId id = StorageResourceId.fromUriPath(uri, false);","typeGuard":"static boolean hasObjectName(URI u) {\n  String p = u.getPath();\n  return p != null && !p.isEmpty() && !p.equals(\"/\");\n}","tryCatchPattern":"catch IllegalArgumentException with message contains(\"must include non-empty object name\") - append the intended object name to the URI, or switch to an API/context that permits directory paths (allowEmptyObjectName=true).","preventionTips":["Never pass bucket-root URIs (gs://bucket or gs://bucket/) to file-level APIs.","Validate that computed filenames are non-empty before building URIs.","Respect the file-vs-directory distinction in URI flags when calling fromUriPath."],"tags":["gcs","object-name","uri","validation"],"backgroundTag":"invalid-object-name","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}