{"record":{"id":"2e17aa3bada95a94","repo":"apache/hadoop","slug":"path-path-is-not-a-symbolic-link-2e17aa","errorCode":null,"errorMessage":"Path ${path} is not a symbolic link","messagePattern":"Path (.+?) is not a symbolic link","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/protocol/HdfsLocatedFileStatus.java","lineNumber":121,"sourceCode":"    super.setOwner(owner);\n  }\n\n  @Override // visibility\n  public void setGroup(String group) {\n    super.setOwner(group);\n  }\n\n  @Override\n  public boolean isSymlink() {\n    return uSymlink != null && uSymlink.length > 0;\n  }\n\n  @Override\n  public Path getSymlink() throws IOException {\n    if (isSymlink()) {\n      return new Path(DFSUtilClient.bytes2String(getSymlinkInBytes()));\n    }\n    throw new IOException(\"Path \" + getPath() + \" is not a symbolic link\");\n  }\n\n  @Override // visibility\n  public void setPermission(FsPermission permission) {\n    super.setPermission(permission);\n  }\n\n  /**\n   * Get the Java UTF8 representation of the local name.\n   * @return the local name in java UTF8\n   */\n  @Override\n  public byte[] getLocalNameInBytes() {\n    return uPath;\n  }\n\n  @Override\n  public void setSymlink(Path sym) {","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/protocol/HdfsLocatedFileStatus.java#L103-L139","documentation":"HdfsLocatedFileStatus.getSymlink() throws when the status is not a symlink: isSymlink() is true only when a link target byte array is present. FileStatus.getSymlink() is only legal on entries for which isSymlink() returned true; this IOException enforces that contract at call time.","triggerScenarios":"Calling getSymlink() on a status obtained from listStatus/getFileStatus for a regular file or directory; also symlink entries whose target byte array is empty (length 0) are treated as non-symlinks.","commonSituations":"Generic code iterating listStatus results and unconditionally reading targets; migrations from local filesystem paths where FileStatusHelper behavior differs; symlink entries serialized without a target.","solutions":["Guard with isSymlink() before calling getSymlink().","Filter with FileContext/utilities that return link targets only for links (e.g. qualify paths then check isSymlink).","If an entry you know is a link still reports false, inspect how the status was created (empty target bytes)."],"exampleFix":"// before\nfor (FileStatus st : fs.listStatus(p)) {\n  Path target = st.getSymlink(); // IOException on regular files\n}\n\n// after\nfor (FileStatus st : fs.listStatus(p)) {\n  if (st.isSymlink()) {\n    Path target = st.getSymlink();\n  }\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static Optional<Path> symlinkTarget(FileStatus st) {\n  return (st != null && st.isSymlink()) ? Optional.of(st.getSymlink()) : Optional.empty();\n}","tryCatchPattern":"try { Path t = st.getSymlink(); }\ncatch (IOException e) { /* status is not a symlink — restructure to check isSymlink() first */ }","preventionTips":["Always branch on isSymlink() before getSymlink().","Wrap the pattern in one helper (like the typeGuard above) and ban direct getSymlink() calls in review.","When constructing statuses in tests, populate the symlink target bytes."],"tags":["hdfs","symlink","file-status","api-misuse"],"backgroundTag":"invalid-state-access","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}