{"record":{"id":"d5f3db92549f965e","repo":"Konloch/bytecode-viewer","slug":"factory-doesn-t-key-for-key-key","errorCode":null,"errorMessage":"factory doesn't key for key: ${key}","messagePattern":"factory doesn't key for key: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/main/java/the/bytecode/club/bytecodeviewer/bootloader/loader/AbstractLoaderFactory.java","lineNumber":65,"sourceCode":"\n        if (FACTORY_CACHE.containsKey(key))\n        {\n            throw new IllegalArgumentException(\"factory already registered with key: \" + key);\n        }\n\n        FACTORY_CACHE.put(key, factory);\n    }\n\n    public static void unregister(String key)\n    {\n        if (key == null)\n        {\n            throw new IllegalArgumentException(\"null key\");\n        }\n\n        if (!FACTORY_CACHE.containsKey(key))\n        {\n            throw new IllegalArgumentException(\"factory doesn't key for key: \" + key);\n        }\n\n        FACTORY_CACHE.remove(key);\n    }\n\n    public static <T extends ExternalResource<?>> LoaderFactory<T> find()\n    {\n        return find(DEFAULT_KEY);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T extends ExternalResource<?>> LoaderFactory<T> find(String key)\n    {\n        if (key == null)\n        {\n            throw new IllegalArgumentException(\"null key\");\n        }\n","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/Konloch/bytecode-viewer/blob/31430e0033fa220db566b5ef461256727ff6793b/src/main/java/the/bytecode/club/bytecodeviewer/bootloader/loader/AbstractLoaderFactory.java#L47-L83","documentation":"Thrown by AbstractLoaderFactory.unregister when no factory is registered under the given key. Unregister is intentionally strict: removing a key that is absent means caller state and registry state have diverged. (Note the message has a typo: \"factory doesn't key for key\".)","triggerScenarios":"Calling unregister(\"x\") when \"x\" was never registered, was already unregistered (double teardown), or the static cache was cleared between registration and removal, e.g. two shutdown hooks both unregistering the same key.","commonSituations":"Application shutdown where cleanup runs twice (context close + JVM shutdown hook); test suites sharing a static registry across test classes; renaming a loader key in config without updating the unregister call.","solutions":["Check registration existence before removing (or track keys you registered yourself).","Make unregister idempotent in your code by catching/swallowing this specific case during teardown.","Ensure only one cleanup path owns registry teardown."],"exampleFix":"// before\ntearDownHooks.forEach(k -> unregister(k));\n// after\ntearDownHooks.stream().filter(k -> isRegistered(k)).forEach(k -> unregister(k)); // or try { unregister(k); } catch (IllegalArgumentException e) { /* already gone */ }","handlingStrategy":"try-catch","validationCode":"// only unregister keys you registered yourself in this run\nif (myRegisteredKeys.remove(key)) AbstractLoaderFactory.unregister(key);","typeGuard":null,"tryCatchPattern":"try { AbstractLoaderFactory.unregister(key); }\ncatch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"factory doesn't\")) {\n        log.debug(\"already unregistered: {}\", key); // idempotent teardown\n    } else throw e;\n}","preventionTips":["Make teardown idempotent — double cleanup paths are common","Let one owner (a single shutdown hook) manage registry lifecycle","Treat unknown-key unregister during shutdown as a warning, not a crash","Keep key strings in constants shared with register"],"tags":["java","loader","registry","unknown-key"],"backgroundTag":"unregistered-key-lookup","analyzedSha":"31430e0033fa220db566b5ef461256727ff6793b","analyzedAt":"2026-09-05T18:22:22.725Z","contentChangedAt":"2026-09-05T18:22:22.725Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}