{"record":{"id":"35dc00403682256e","repo":"apache/iceberg","slug":"unable-to-list-metadata-directory","errorCode":null,"errorMessage":"Unable to list metadata directory {}","messagePattern":"Unable to list metadata directory (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/src/main/java/org/apache/iceberg/hadoop/HadoopCatalog.java","lineNumber":167,"sourceCode":"    if (suppressPermissionError) {\n      return ioException instanceof AccessDeniedException\n          || (ioException.getMessage() != null\n              && ioException.getMessage().contains(\"AuthorizationPermissionMismatch\"));\n    }\n    return false;\n  }\n\n  private boolean isTableDir(Path path) {\n    Path metadataPath = new Path(path, \"metadata\");\n    // Only the path which contains metadata is the path for table, otherwise it could be\n    // still a namespace.\n    try {\n      return fs.listStatus(metadataPath, TABLE_FILTER).length >= 1;\n    } catch (FileNotFoundException e) {\n      return false;\n    } catch (IOException e) {\n      if (shouldSuppressPermissionError(e)) {\n        LOG.warn(\"Unable to list metadata directory {}\", metadataPath, e);\n        return false;\n      } else {\n        throw new UncheckedIOException(e);\n      }\n    }\n  }\n\n  private boolean isDirectory(Path path) {\n    try {\n      return fs.getFileStatus(path).isDirectory();\n    } catch (FileNotFoundException e) {\n      return false;\n    } catch (IOException e) {\n      if (shouldSuppressPermissionError(e)) {\n        LOG.warn(\"Unable to list directory {}\", path, e);\n        return false;\n      } else {\n        throw new UncheckedIOException(e);","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/hadoop/HadoopCatalog.java#L149-L185","documentation":"HadoopCatalog.isTableDir logs this WARN when listing a table's metadata directory for version-hint/metadata files fails with an IOException that is classified as a permission error. The method returns false (treats the directory as not a table) instead of throwing, which can silently hide tables from listTables results.","triggerScenarios":"listTables or isNamespace walks candidate directories; fs.listStatus on <table>/metadata throws an access-denied IOException and shouldSuppressPermissionError(e) is true (fs.permissions.suppress.* config or default behavior).","commonSituations":"HDFS/S3 ACLs limiting the listing user; Kerberos/permission misconfiguration; catalog listing running under a service account without read access to some namespaces.","solutions":["Grant the catalog/execution user read+execute permissions on the table metadata directories.","Fix HDFS ACL/Kerberos or S3 IAM policies so listing is allowed.","If the suppression is undesirable, adjust shouldSuppressPermissionError configuration to surface the error instead of returning false."],"exampleFix":"// before (HDFS)\nhdfs dfs -chmod -R 750 /warehouse/db/table\n// after\nhdfs dfs -chmod -R 755 /warehouse/db/table  # or grant the service account via setfacl","handlingStrategy":"validation","validationCode":"// check access before listing\n// hdfs dfs -test -d /warehouse/db/table/metadata\n// or in code: fs.access(metadataPath, FsAction.READ_EXECUTE)","typeGuard":null,"tryCatchPattern":"try {\n  catalog.listTables(Namespace.of(\"db\"));\n} catch (UncheckedIOException e) {\n  LOG.error(\"metadata dir not listable: {}\", e.getCause());\n}","preventionTips":["Grant the catalog user r-x on warehouse directories.","Keep namespace/table directories world-traversable (execute bit) where appropriate.","Align Kerberos principals/service accounts with directory owners."],"tags":["hadoop","permissions","io"],"backgroundTag":"permission-denied","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}