{"record":{"id":"d88c46f66ac3dfc4","repo":"apache/hadoop","slug":"unable-to-parse-permission-string-expected-3-c","errorCode":null,"errorMessage":"Unable to parse permission string {}: expected 3 components, but only had {}","messagePattern":"Unable to parse permission string (.+?): expected 3 components, but only had (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/OfflineImageReconstructor.java","lineNumber":1610,"sourceCode":"        bld.build().writeDelimitedTo(out);\n      }\n      expectTagEnd(SNAPSHOT_DIFF_SECTION_FILE_DIFF_ENTRY);\n    }\n  }\n\n  /**\n   * Permission is serialized as a 64-bit long. [0:24):[25:48):[48:64)\n   * (in Big Endian).  The first and the second parts are the string ids\n   * of the user and group name, and the last 16 bits are the permission bits.\n   *\n   * @param perm           The permission string from the XML.\n   * @return               The 64-bit value to use in the fsimage for permission.\n   * @throws IOException   If we run out of string IDs in the string table.\n   */\n  private long permissionXmlToU64(String perm) throws IOException {\n    String components[] = perm.split(\":\");\n    if (components.length != 3) {\n      throw new IOException(\"Unable to parse permission string \" + perm +\n          \": expected 3 components, but only had \" + components.length);\n    }\n    String userName = components[0];\n    String groupName = components[1];\n    String modeString = components[2];\n    long userNameId = registerStringId(userName);\n    long groupNameId = registerStringId(groupName);\n    long mode = new FsPermission(modeString).toShort();\n    return (userNameId << 40) | (groupNameId << 16) | mode;\n  }\n\n  /**\n   * The FSImage contains a string table which maps strings to IDs.\n   * This is a simple form of compression which takes advantage of the fact\n   * that the same strings tend to occur over and over again.\n   * This function will return an ID which we can use to represent the given\n   * string.  If the string already exists in the string table, we will use\n   * that ID; otherwise, we will allocate a new one.","sourceCodeStart":1592,"sourceCodeEnd":1628,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/tools/offlineImageViewer/OfflineImageReconstructor.java#L1592-L1628","documentation":"permissionXmlToU64 splits an inode's <permission> string on ':' and requires exactly three components - user, group, and symbolic mode (e.g. hdfs:supergroup:rwxr-xr-x) - to pack into the 64-bit permission long. The split produced a different component count, so the string cannot be encoded and the inode cannot be written.","triggerScenarios":"A <permission> value like 'rwxr-xr-x' (mode only), 'hdfs' (user only), 'hdfs:supergroup' (missing mode), or one with extra colons; any hand-edited or script-injected permission field that breaks the user:group:mode shape.","commonSituations":"Permissions rewritten during bulk edits; empty <permission/> elements from faulty transforms; usernames or groupnames that themselves contain ':' (unsupported by this format).","solutions":["Set every <permission> to full 'USER:GROUP:MODE' form, e.g. hdfs:supergroup:rwxr-xr-x","Grep the XML for values that do not match ^[^:]+:[^:]+:[rwxstX-]{9,10}$ and fix each","If a username legitimately contains ':', it cannot be represented in this format - rewrite it or regenerate from the original image","Validate all permission strings with a streaming pre-check before ReverseXML"],"exampleFix":"<!-- before -->\n<permission>rwxr-xr-x</permission>\n<!-- after -->\n<permission>hdfs:supergroup:rwxr-xr-x</permission>","handlingStrategy":"validation","validationCode":"# python: all <permission> values must be USER:GROUP:MODE\nimport re, xml.etree.ElementTree as ET\nPERM_RE = re.compile(r'^[^:]+:[^:]+:[rwxstX-]{9,10}$')\n\ndef permissions_ok(path):\n    bad = []\n    for ev, el in ET.iterparse(path, events=('end',)):\n        if el.tag == 'permission' and not PERM_RE.match(el.text or ''):\n            bad.append(el.text)\n    return not bad","typeGuard":"def is_valid_permission(s: str) -> bool:\n    \"\"\"Narrow a <permission> text node to the USER:GROUP:MODE shape oiv accepts.\"\"\"\n    parts = s.split(':')\n    return len(parts) == 3 and all(parts) and set(parts[2]) <= set('rwxstX-')","tryCatchPattern":"# catch the parse failure naming the offending string, fix that\n# <permission> element, remove partial output, re-run","preventionTips":["Always write permissions in full user:group:mode form","Bulk-validate with the regex before reconstruction","Usernames containing ':' are unrepresentable - remap them first","Prefer regenerating XML over rewriting permission fields by hand"],"tags":["hdfs","oiv","fsimage","reversexml","permission","validation"],"backgroundTag":"hdfs-permission-string-parse-failed","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}