{"record":{"id":"08b2675679161194","repo":"karatelabs/karate","slug":"identifier-name-has-already-been-declared","errorCode":null,"errorMessage":"identifier '${name}' has already been declared","messagePattern":"identifier '(.+?)' has already been declared","errorType":"exception","errorClass":"ParserException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/parser/JsParser.java","lineNumber":1268,"sourceCode":"                }\n            } else if (d.type == NodeType.FN_EXPR && !topLevel) {\n                String name = declarationName(d);\n                if (name != null) {\n                    lexNames.add(name); // function-bound — deliberately not in lexNonFn\n                }\n            }\n        }\n        if (lexNames.isEmpty()) {\n            return; // no lexical declarations → no duplicate and no clash possible\n        }\n        // Duplicate LexicallyDeclaredNames. Annex B.3.3: a sloppy duplicate bound only\n        // by FunctionDeclarations is allowed; everything else (and all of strict) errors.\n        Set<String> seen = new HashSet<>();\n        for (String name : lexNames) {\n            if (!seen.add(name)) {\n                boolean onlyFunctions = !lexNonFn.contains(name);\n                if (strict || !onlyFunctions) {\n                    throw new ParserException(\n                            \"identifier '\" + name + \"' has already been declared\");\n                }\n            }\n        }\n        // Lexical names may not also be var-declared anywhere in the scope (vars hoist\n        // through nested blocks; at top level a FunctionDeclaration is itself var-scoped).\n        List<String> varNames = new ArrayList<>();\n        for (Node stmt : statements) {\n            collectVarNames(stmt, varNames);\n            if (topLevel) {\n                Node d = firstNonToken(stmt);\n                if (d != null && d.type == NodeType.FN_EXPR) {\n                    String name = declarationName(d);\n                    if (name != null) {\n                        varNames.add(name);\n                    }\n                }\n            }","sourceCodeStart":1250,"sourceCodeEnd":1286,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/parser/JsParser.java#L1250-L1286","documentation":"This is a strict-mode-style early error from the Karate JS parser: a lexical declaration (let/const/class) re-declares an identifier that was already lexically declared in the same scope. JavaScript forbids duplicate lexical bindings; in sloppy mode duplicate function declarations are tolerated, but in strict mode (Karate's default) any duplicate errors at parse time. It is thrown before any code executes, so nothing of the script runs.","triggerScenarios":"Parsing a script where two let/const/class declarations in the same block use the same name, e.g. `let x = 1; let x = 2;` — the duplicate-name check over lexNames fires when seen.add(name) fails and the name is either a non-function lexical binding or the code is strict.","commonSituations":"Copy-pasting a variable declaration into a Karate JS block that already declares it; refactoring that leaves both `let` and `const` for the same name in one function or block; converting `var` to `let` in two places without noticing the collision.","solutions":["Rename one of the conflicting declarations so each lexical name is unique per scope","Drop the redundant `let`/`const` keyword and just reassign the existing binding (e.g. `x = 2;`)","Move one declaration into a nested block `{ ... }` if a separate binding is genuinely intended","Check for accidental duplicated paste of a declaration line or block"],"exampleFix":"// before\nlet temp = a;\nlet temp = b; // duplicate\n// after\nlet tempA = a;\nlet tempB = b;","handlingStrategy":"validation","validationCode":"// before running a Karate JS snippet, check for duplicate let/const/class names per scope\nfunction hasDuplicateLexicalDecls(src) {\n  const decls = [...src.matchAll(/\\b(?:let|const|class)\\s+([A-Za-z_$][\\w$]*)/g)].map(m => m[1]);\n  return decls.length !== new Set(decls).size;\n}","typeGuard":null,"tryCatchPattern":"try {\n  karate.configure('...', jsSnippet);\n} catch (e) {\n  if (String(e).includes('has already been declared')) {\n    // surface the offending name and fix the script\n  }\n  throw e;\n}","preventionTips":["Use a linter (ESLint no-redeclare) on embedded JS before deployment","Prefer a single declaration keyword style per scope (all let/const)","Avoid copy-pasting blocks that redeclare names already in scope"],"tags":["javascript","parser","strict-mode","duplicate-declaration"],"backgroundTag":"invalid-identifier","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}