{"record":{"id":"73416bbd850dd4d3","repo":"apache/hadoop","slug":"cannot-create-a-file-whose-name-looks-like-a-direc","errorCode":null,"errorMessage":"Cannot create a file whose name looks like a directory: '%s'","messagePattern":"Cannot create a file whose name looks like a directory: '(.+?)'","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorageFileSystem.java","lineNumber":113,"sourceCode":"\n    return new GoogleCloudStorage(configuration, credentials);\n  }\n\n  GoogleCloudStorageFileSystem(final GoogleHadoopFileSystemConfiguration configuration,\n      final Credentials credentials) throws IOException {\n    this.configuration = configuration;\n    gcs = createCloudStorage(configuration, credentials);\n  }\n\n  WritableByteChannel create(final URI path, final CreateFileOptions createOptions)\n      throws IOException {\n    LOG.trace(\"create(path: {}, createOptions: {})\", path, createOptions);\n    checkNotNull(path, \"path could not be null\");\n    StorageResourceId resourceId =\n        StorageResourceId.fromUriPath(path, /* allowEmptyObjectName=*/ true);\n\n    if (resourceId.isDirectory()) {\n      throw new IOException(\n          String.format(\"Cannot create a file whose name looks like a directory: '%s'\",\n              resourceId));\n    }\n\n    // Because create call should create parent directories too, before creating an actual file\n    // we need to check if there are no conflicting items in the directory tree:\n    // - if there are no conflicting files with the same name as any parent subdirectory\n    // - if there are no conflicting directory with the name as a file\n    //\n    // For example, for a new `gs://bucket/c/d/f` file:\n    // - files `gs://bucket/c` and `gs://bucket/c/d` should not exist\n    // - directory `gs://bucket/c/d/f/` should not exist\n    if (configuration.isEnsureNoConflictingItems()) {\n      // Check if a directory with the same name exists.\n      StorageResourceId dirId = resourceId.toDirectoryId();\n      Boolean conflictingDirExist = false;\n      if (createOptions.isEnsureNoDirectoryConflict()) {\n        // TODO: Do this concurrently","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorageFileSystem.java#L95-L131","documentation":"GoogleCloudStorageFileSystem.create throws when StorageResourceId.fromUriPath(path, allowEmptyObjectName=true) yields a directory id — an empty object name, i.e. a path with a trailing '/'. In GCS, directories are naming conventions (prefixes and placeholder objects), so a file whose name 'looks like' a directory is rejected before any parent-conflict checks run.","triggerScenarios":"Calling create(gs://bucket/some/dir/, options) — any path whose object component is empty because of a trailing slash; URIs assembled by joining path segments that already end with '/'.","commonSituations":"String-built paths with duplicated or trailing separators; user-supplied filenames not normalized; code ported from local filesystems where a trailing slash is ignored; configuration values with accidental trailing '/'.","solutions":["Normalize the path and strip trailing '/' from the object name before create.","Build URIs with path utilities (UriPaths) instead of string concatenation so files and directories cannot blur.","If a directory was intended, call mkdirs instead of create."],"exampleFix":"// before\nURI path = URI.create(\"gs://bucket/data/part-0000/\");\ngcsFs.create(path, CreateFileOptions.DEFAULT); // 'looks like a directory'\n\n// after\nString p = path.toString().replaceAll(\"/+$\", \"\");\ngcsFs.create(URI.create(p), CreateFileOptions.DEFAULT);","handlingStrategy":"validation","validationCode":"static URI asFilePath(URI u) {\n  String s = u.toString();\n  checkArgument(!s.equals(\"gs://\"), \"need bucket + object\");\n  return URI.create(s.replaceAll(\"/+$\", \"\")); // strip trailing '/' for file create\n}\ngcsFs.create(asFilePath(path), CreateFileOptions.DEFAULT);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build URIs with path utilities rather than string concatenation.","Normalize user-supplied filenames (strip trailing slashes and empty segments) at the API boundary.","Add a unit test asserting create() is never called with a path ending in '/'."],"tags":["gcs","create","path-validation","trailing-slash"],"backgroundTag":"path-looks-like-directory","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}