{"record":{"id":"52e6d20515231d32","repo":"quarkusio/quarkus","slug":"immutable-empty-scope","errorCode":null,"errorMessage":"Immutable empty scope","messagePattern":"Immutable empty scope","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"independent-projects/qute/core/src/main/java/io/quarkus/qute/Scope.java","lineNumber":11,"sourceCode":"package io.quarkus.qute;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class Scope {\n\n    public static final Scope EMPTY = new Scope(null) {\n        @Override\n        public void putBinding(String binding, String type) {\n            throw new UnsupportedOperationException(\"Immutable empty scope\");\n        }\n    };\n\n    private final Scope parentScope;\n    private Map<String, String> bindings;\n    private Map<String, Object> attributes;\n    // TODO: add proper API to handle this\n    private String lastPartHint;\n\n    public Scope(Scope parentScope) {\n        this.parentScope = parentScope;\n    }\n\n    public void putBinding(String binding, String type) {\n        if (bindings == null) {\n            bindings = new HashMap<>();\n        }\n        bindings.put(binding, sanitizeType(type));","sourceCodeStart":1,"sourceCodeEnd":29,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/qute/core/src/main/java/io/quarkus/qute/Scope.java#L1-L29","documentation":"Scope.EMPTY is an immutable sentinel scope used at the root of template parsing; any attempt to add a binding to it throws UnsupportedOperationException(\"Immutable empty scope\"). It exists so code can safely read from a shared, always-empty scope.","triggerScenarios":"Calling putBinding(...) on Scope.EMPTY — reached from TemplateGenerator/Parser paths such as initializeBlock, putMetadataBinding, parameterDeclaration, addParameter, or assertBinding when the current scope is the immutable empty root.","commonSituations":"Custom parser hooks or template extensions that add metadata/parameter declarations at the template root level where only the immutable scope exists; library bugs where a section should have created a child scope before binding.","solutions":["Create a child scope (scope.child()) before calling putBinding instead of binding on the root Scope.EMPTY.","Check scope == Scope.EMPTY (or a canWrite check) before putBinding and fall back to a child scope.","If triggered by ordinary templates (no custom hooks), report it — the parser should have opened a mutable scope."],"exampleFix":"// before\nscope.putBinding(\"item\", \"String\"); // may be Scope.EMPTY\n\n// after\nif (scope == Scope.EMPTY) {\n    scope = scope.child();\n}\nscope.putBinding(\"item\", \"String\");","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static boolean isMutableScope(Scope scope) {\n    return scope != Scope.EMPTY;\n}","tryCatchPattern":"try {\n    scope.putBinding(name, type);\n} catch (UnsupportedOperationException e) {\n    if (\"Immutable empty scope\".equals(e.getMessage())) {\n        scope.child().putBinding(name, type);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always create a child scope before binding in custom parser hooks","Check scope != Scope.EMPTY before mutating","Root-level metadata should go through supported APIs, not direct binding writes"],"tags":["qute","immutable","unsupported-operation","scope"],"backgroundTag":"immutable-scope-write","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}