{"record":{"id":"105d4ffb3d89f8cf","repo":"provectus/kafka-ui","slug":"file-already-exists-and-is-not-writable","errorCode":null,"errorMessage":"File already exists and is not writable","messagePattern":"File already exists and is not writable","errorType":"validation","errorClass":"ValidationException","httpStatus":400,"severity":"error","filePath":"kafka-ui-api/src/main/java/com/provectus/kafka/ui/util/DynamicConfigOperations.java","lineNumber":180,"sourceCode":"\n  private void checkIfDynamicConfigEnabled() {\n    if (!dynamicConfigEnabled()) {\n      throw new ValidationException(\n          \"Dynamic config change is not allowed. \"\n              + \"Set dynamic.config.enabled property to 'true' to enabled it.\");\n    }\n  }\n\n  @SneakyThrows\n  private void writeYamlToFile(String yaml, Path path) {\n    if (Files.isDirectory(path)) {\n      throw new ValidationException(\"Dynamic file path is a directory, but should be a file path\");\n    }\n    if (!Files.exists(path.getParent())) {\n      Files.createDirectories(path.getParent());\n    }\n    if (Files.exists(path) && !Files.isWritable(path)) {\n      throw new ValidationException(\"File already exists and is not writable\");\n    }\n    try {\n      Files.writeString(\n          path,\n          yaml,\n          StandardOpenOption.CREATE,\n          StandardOpenOption.WRITE,\n          StandardOpenOption.TRUNCATE_EXISTING // to override existing file\n      );\n    } catch (IOException e) {\n      throw new ValidationException(\"Error writing to \" + path, e);\n    }\n  }\n\n  private String serializeToYaml(PropertiesStructure props) {\n    //representer, that skips fields with null values\n    Representer representer = new Representer(new DumperOptions()) {\n      @Override","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/provectus/kafka-ui/blob/83b5a60cc08501b570a0c4d0b4cdfceb1b88d6b7/kafka-ui-api/src/main/java/com/provectus/kafka/ui/util/DynamicConfigOperations.java#L162-L198","documentation":"After ensuring the target path is a file and creating parent directories, writeYamlToFile() checks that an existing config file is writable via Files.isWritable(). If the file exists but the process has no write permission on it, persisting the dynamic config throws this ValidationException.","triggerScenarios":"dynamic.config.path points to an already-created file owned by another user or opened read-only (e.g. a read-only mounted secret/ConfigMap file) while kafka-ui tries to save new properties.","commonSituations":"Container runs as non-root but the config file was created by root; the file lives on a read-only volume mount; file permissions like 0444 set by a provisioning script.","solutions":["chown/chmod the config file so the kafka-ui process user can write it (e.g. chmod u+w, chown to the container UID)","Move dynamic.config.path to a writable volume instead of a read-only mount","Ensure the container's securityContext allows writing to that path"],"exampleFix":"// before\n-r--r--r-- application-config.yml\n// after (shell)\nchmod u+w application-config.yml && chown kafka-ui:kafka-ui application-config.yml","handlingStrategy":"validation","validationCode":"Path p = Path.of(configPath);\nif (Files.exists(p) && !Files.isWritable(p)) {\n  throw new IllegalStateException(\"Config file exists but is not writable by this process\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  dynamicConfigOperations.persist(props);\n} catch (ValidationException e) {\n  if (e.getMessage().contains(\"not writable\")) {\n    // chmod/chown or relocate to a writable volume\n  }\n}","preventionTips":["Pre-create the config file with correct ownership in your deployment","Avoid read-only mounts for the dynamic config path","Match the file's UID/GID to the container's runtime user"],"tags":["filesystem","permissions","dynamic-config"],"backgroundTag":"file-write-permission-denied","analyzedSha":"83b5a60cc08501b570a0c4d0b4cdfceb1b88d6b7","analyzedAt":"2026-09-08T04:35:39.002Z","contentChangedAt":"2026-09-08T04:35:39.002Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}