{"record":{"id":"8b6dade8573f65ad","repo":"hashicorp/nomad","slug":"file-s-cannot-be-mapped-only-directories-can-b","errorCode":null,"errorMessage":"file '%s' cannot be mapped. Only directories can be mapped on this platform","messagePattern":"file '(.+?)' cannot be mapped\\. Only directories can be mapped on this platform","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/docker/win32_volume_parse.go","lineNumber":146,"sourceCode":"\t\t}\n\t}\n\t// Fix #26329. If the destination appears to be a file, and the source is null,\n\t// it may be because we've fallen through the possible naming regex and hit a\n\t// situation where the user intention was to map a file into a container through\n\t// a local volume, but this is not supported by the platform.\n\tif matchgroups[\"source\"] == \"\" && matchgroups[\"destination\"] != \"\" {\n\t\tvolExp := regexp.MustCompile(`^` + rxName + `$`)\n\t\treservedNameExp := regexp.MustCompile(`^` + rxReservedNames + `$`)\n\n\t\tif volExp.MatchString(matchgroups[\"destination\"]) {\n\t\t\tif reservedNameExp.MatchString(matchgroups[\"destination\"]) {\n\t\t\t\treturn nil, fmt.Errorf(\"volume name %q cannot be a reserved word for Windows filenames\", matchgroups[\"destination\"])\n\t\t\t}\n\t\t} else {\n\n\t\t\texists, isDir, _ := currentFileInfoProvider.fileInfo(matchgroups[\"destination\"])\n\t\t\tif exists && !isDir {\n\t\t\t\treturn nil, fmt.Errorf(\"file '%s' cannot be mapped. Only directories can be mapped on this platform\", matchgroups[\"destination\"])\n\n\t\t\t}\n\t\t}\n\t}\n\treturn split, nil\n}\n","sourceCodeStart":128,"sourceCodeEnd":153,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/drivers/docker/win32_volume_parse.go#L128-L153","documentation":"Windows bind mounts only support directories, so when the destination token of a spec is an existing path on disk that is a file (not a directory), windowsSplitRawSpec rejects it. The existence check uses the injected fileInfoProvider before returning split parts.","triggerScenarios":"Spec like \"C:\\host:C:\\some\\file.txt\" where C:\\some\\file.txt exists and is a regular file; the parser refuses to map a file as a mount target on Windows.","commonSituations":"Mounting a single-file config (works on Linux bind mounts) into a container on a Windows host; destination path created as a file by a previous run instead of a directory.","solutions":["Mount the file's parent directory instead, or switch to copying the file into the image/template stanza.","Create the destination as a directory (mkdir) before the job runs.","If you intended a named volume, verify the token isn't colliding with an existing file of the same name."],"exampleFix":"// before\nvolumes = [\"C:/configs:C:/app/config.txt\"]\n// after\nvolumes = [\"C:/configs:C:/app/configs\"] // mount directory, deliver file via template","handlingStrategy":"validation","validationCode":"if fi, err := os.Stat(destPath); err == nil && !fi.IsDir() {\n  return fmt.Errorf(\"destination %s is a file; Windows mounts require directories\", destPath)\n}","typeGuard":null,"tryCatchPattern":"if err := ensureDestIsDir(dest); err != nil {\n  return fmt.Errorf(\"cannot mount: %w\", err)\n}","preventionTips":["Pre-mount the target as a directory on Windows hosts.","Use template stanzas instead of single-file bind mounts.","Verify paths after re-provisioning hosts — leftover files at mount points recur."],"tags":["docker","volumes","windows","filesystem"],"backgroundTag":"invalid-volume-spec","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}