{"record":{"id":"255db065e19765fc","repo":"apache/dolphinscheduler","slug":"file-extension-cannot-not-change","errorCode":null,"errorMessage":"file extension cannot not change","messagePattern":"file extension cannot not change","errorType":"validation","errorClass":"ServiceException","httpStatus":null,"severity":"error","filePath":"dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileDtoValidator.java","lineNumber":50,"sourceCode":"\n@Component\npublic class UpdateFileDtoValidator extends AbstractResourceValidator<UpdateFileDto> {\n\n    public UpdateFileDtoValidator(StorageOperator storageOperator, TenantDao tenantDao) {\n        super(storageOperator, tenantDao);\n    }\n\n    @Override\n    public void validate(UpdateFileDto updateFileDto) {\n        String fileAbsolutePath = updateFileDto.getFileAbsolutePath();\n        User loginUser = updateFileDto.getLoginUser();\n        MultipartFile file = updateFileDto.getFile();\n\n        if (!Objects.equals(\n                Files.getFileExtension(\n                        file.getOriginalFilename() == null ? file.getName() : file.getOriginalFilename()),\n                Files.getFileExtension(updateFileDto.getFileAbsolutePath()))) {\n            throw new ServiceException(\"file extension cannot not change\");\n        }\n\n        exceptionResourceAbsolutePathInvalidated(fileAbsolutePath);\n        exceptionResourceNotExists(fileAbsolutePath);\n        exceptionResourceIsNotFile(fileAbsolutePath);\n        exceptionUserNoResourcePermission(loginUser, fileAbsolutePath);\n        exceptionFileInvalidated(file);\n    }\n}\n","sourceCodeStart":32,"sourceCodeEnd":60,"githubUrl":"https://github.com/apache/dolphinscheduler/blob/02eac45a1b6676e639fcbfb4be2243de5771b05d/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/validator/resource/UpdateFileDtoValidator.java#L32-L60","documentation":"UpdateFileDtoValidator.validate enforces that when updating a resource file, the replacement file uploaded via multipart has the same file extension as the existing resource at fileAbsolutePath. If Files.getFileExtension of the uploaded file's name differs from the extension of the target resource path, a ServiceException('file extension cannot not change') is thrown and the update is rejected. This prevents users from silently converting a resource's type (e.g. replacing a .py script with a .jar).","triggerScenarios":"Calling the resource-update API (UpdateResourceController / updateResource) with an UpdateFileDto whose uploaded MultipartFile has an original filename whose extension differs from the extension of fileAbsolutePath, e.g. updating resources/script.py with a file named script.txt.","commonSituations":"Renaming or re-saving a file locally before upload so the extension changes (export.csv -> export.xlsx); uploading a temp file without its extension (browser stripping .py); picking the wrong file in the UI; drag-and-drop from tools that append extensions like .download or .crdownload.","solutions":["Re-upload a file whose name ends with the same extension as the existing resource path (match getFileExtension of fileAbsolutePath).","Rename the uploaded file before the request: new File(\"same-name.\" + extensionOfTarget).","If a type change is genuinely needed, delete the old resource and create a new one with the correct extension instead of updating.","Ensure the HTTP client preserves the original filename in multipart Content-Disposition (set filename explicitly)."],"exampleFix":"// before: uploading replacement with mismatched name\nfile = new File(\"/tmp/script.txt\"); // target resource is script.py\n// after\nFile fixed = new File(\"/tmp/script.py\"); // same extension as fileAbsolutePath\nupdateFileDto.setFile(fixed);","handlingStrategy":"validation","validationCode":"const targetExt = filePath.split('.').pop().toLowerCase();\nconst uploadExt = (file.name || '').split('.').pop().toLowerCase();\nif (targetExt !== uploadExt) {\n  throw new Error(`Extension mismatch: target .${targetExt}, upload .${uploadExt}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await updateResource(dto);\n} catch (e) {\n  if (e.message.includes('file extension cannot not change')) {\n    // surface: rename file to match resource extension and retry\n  }\n  throw e;\n}","preventionTips":["Always derive the uploaded filename from the original resource name, not from a local temp file.","Verify multipart clients preserve Content-Disposition filename.","Delete-and-recreate resources when a type change is intentional.","Check the file picker for auto-appended extensions like .download."],"tags":["file-upload","validation","resources"],"backgroundTag":"invalid-argument-value","analyzedSha":"02eac45a1b6676e639fcbfb4be2243de5771b05d","analyzedAt":"2026-09-06T17:43:00.555Z","contentChangedAt":"2026-09-06T17:43:00.555Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}