{"record":{"id":"8a71c3d2171b85fa","repo":"Grasscutters/Grasscutter","slug":"failed-to-load-handbook-auth-page","errorCode":null,"errorMessage":"Failed to load handbook auth page.","messagePattern":"Failed to load handbook auth page\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/main/java/emu/grasscutter/auth/DefaultAuthenticators.java","lineNumber":382,"sourceCode":"            client.sendMessage(PacketIds.TokenValidateReq, tokenRequest);\n\n            try {\n                return future.get(5, TimeUnit.SECONDS);\n            } catch (Exception ignored) {\n                return null;\n            }\n        }\n    }\n\n    /** Handles authentication for the web GM Handbook. */\n    public static class HandbookAuthentication implements HandbookAuthenticator {\n        private final String authPage;\n\n        public HandbookAuthentication() {\n            try {\n                this.authPage = new String(FileUtils.readResource(\"/html/handbook_auth.html\"));\n            } catch (Exception ignored) {\n                throw new RuntimeException(\"Failed to load handbook auth page.\");\n            }\n        }\n\n        @Override\n        public void presentPage(AuthenticationRequest request) {\n            var ctx = request.getContext();\n            if (ctx == null) return;\n\n            // Check to see if an IP authentication can be performed.\n            if (Grasscutter.getRunMode() == ServerRunMode.HYBRID) {\n                var player = Grasscutter.getGameServer().getPlayerByIpAddress(Utils.address(ctx));\n                if (player != null) {\n                    // Get the player's session token.\n                    var sessionKey = player.getAccount().getSessionKey();\n                    // Respond with the handbook auth page.\n                    ctx.status(200)\n                            .result(\n                                    this.authPage","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/Grasscutters/Grasscutter/blob/f373827a83d3e688fd3c6afb1e1c3759ca1d61c9/src/main/java/emu/grasscutter/auth/DefaultAuthenticators.java#L364-L400","documentation":"Thrown by Grasscutter's HandbookAuthentication authenticator during construction when it cannot read the bundled resource /html/handbook_auth.html. The class eagerly loads the handbook authentication HTML page into a final field; if the resource is missing or unreadable the constructor aborts with this RuntimeException, meaning the handbook auth flow can never present its page.","triggerScenarios":"Constructing the HandbookAuthentication authenticator when /html/handbook_auth.html does not exist in the resources directory, is not extracted by the resource loader (FileUtils.readResource), or cannot be read (permissions/IO error).","commonSituations":"Running a partially-built Grasscutter where resources were not copied into the jar; deleting or renaming the html folder in resources; running from a stripped distribution missing the handbook page; read-permission problems on the install directory.","solutions":["Restore or re-extract /html/handbook_auth.html (rebuild the jar or run Grasscutter's resource extraction so resources are present).","Verify the file exists at src/main/resources/html/handbook_auth.html in the build inputs and that the build copies it into the jar.","Check file system permissions on the Grasscutter installation directory so the process can read resources.","As a workaround, replace the HandbookAuthentication authenticator with another registered AuthenticationSystem authenticator if the handbook is not needed."],"exampleFix":"// before (no page present)\n// java -jar grasscutter.jar  -> RuntimeException: Failed to load handbook auth page.\n// after\n// cp resources/html/handbook_auth.html <install>/html/handbook_auth.html  (or rebuild jar)\n// java -jar grasscutter.jar","handlingStrategy":"try-catch","validationCode":"var res = getClass().getResource(\"/html/handbook_auth.html\");\nif (res == null) throw new IllegalStateException(\"handbook_auth.html missing from classpath/resources\");","typeGuard":"static boolean handbookPageAvailable() {\n    return HandbookAuthentication.class.getResource(\"/html/handbook_auth.html\") != null;\n}","tryCatchPattern":"try (var in = FileUtils.readResource(\"/html/handbook_auth.html\")) {\n    new HandbookAuthentication();\n} catch (RuntimeException | IOException e) {\n    LOGGER.error(\"Handbook auth page unavailable: \" + e.getMessage());\n    // fall back to a different authenticator or disable the handbook endpoint\n}","preventionTips":["Keep resources/html/handbook_auth.html in the repo and verify the build copies it into the jar.","Run resource extraction after updating Grasscutter or switching distributions.","Add a startup check that logs missing resources before the authenticator is constructed.","Don't strip the html directory when packaging or deploying the jar."],"tags":["resources","io","runtime"],"backgroundTag":"missing-resource-file","analyzedSha":"f373827a83d3e688fd3c6afb1e1c3759ca1d61c9","analyzedAt":"2026-09-03T22:43:49.407Z","contentChangedAt":"2026-09-03T22:43:49.407Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}