{"record":{"id":"33ade2be36edd411","repo":"microsoft/TypeScript","slug":"global-option-key-already-exists","errorCode":null,"errorMessage":"Global option '${key}' already exists","messagePattern":"Global option '(.+?)' already exists","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/harness/fourslashImpl.ts","lineNumber":4804,"sourceCode":"        }\r\n        else if (line.substr(0, 3) === \"///\" && currentFileContent !== undefined) {\r\n            throw new Error(\"Three-slash line in the middle of four-slash region at line \" + i);\r\n        }\r\n        else if (line.substr(0, 2) === \"//\") {\r\n            const possiblySymlinks = Harness.TestCaseParser.parseSymlinkFromTest(line, symlinks);\r\n            if (possiblySymlinks) {\r\n                symlinks = possiblySymlinks;\r\n            }\r\n            else {\r\n                // Comment line, check for global/file @options and record them\r\n                const match = optionRegex.exec(line.substr(2));\r\n                if (match) {\r\n                    const key = match[1].toLowerCase();\r\n                    const value = match[2];\r\n                    if (!ts.contains(fileMetadataNames, key)) {\r\n                        // Check if the match is already existed in the global options\r\n                        if (globalOptions[key] !== undefined) {\r\n                            throw new Error(`Global option '${key}' already exists`);\r\n                        }\r\n                        globalOptions[key] = value;\r\n                    }\r\n                    else {\r\n                        switch (key) {\r\n                            case MetadataOptionNames.fileName:\r\n                                // Found an @FileName directive, if this is not the first then create a new subfile\r\n                                nextFile();\r\n                                currentFileName = ts.isRootedDiskPath(value) ? value : basePath + \"/\" + value;\r\n                                currentFileOptions[key] = value;\r\n                                break;\r\n                            case MetadataOptionNames.symlink:\r\n                                currentFileSymlinks = ts.append(currentFileSymlinks, value);\r\n                                break;\r\n                            default:\r\n                                // Add other fileMetadata flag\r\n                                currentFileOptions[key] = value;\r\n                        }\r","sourceCodeStart":4786,"sourceCodeEnd":4822,"githubUrl":"https://github.com/microsoft/TypeScript/blob/b465fdbfe175304d9b977da137b2c178ae1091d3/src/harness/fourslashImpl.ts#L4786-L4822","documentation":"Thrown by parseTestData when a global @option (a // @key: value line outside any @FileName block) is declared more than once. The harness stores global options in a plain object keyed by lower-cased name and refuses to overwrite an existing key. File-metadata keys (those in fileMetadataNames, e.g. @FileName, @symlink) are exempt and handled separately.","triggerScenarios":"A fourslash test file contains two or more // @compilerOption: ... lines with the same key outside of any @FileName directive, so globalOptions[key] is already defined when the second is parsed.","commonSituations":"Merging two test files duplicates a global setting; an author sets the same option twice expecting the later one to win. Configuration keys are case-insensitive so a different casing still collides.","solutions":["Search the test file for repeated @key lines (case-insensitive) and keep only one.","If you intended per-file settings, move the option inside the relevant @FileName block so it is treated as file metadata rather than a global option.","Rename one of the options if they genuinely represent different settings."],"exampleFix":"// before\n// @module: commonjs\n// @module: esnext\n// after\n// @module: esnext","handlingStrategy":"validation","validationCode":"function assertUniqueGlobalOptions(lines: string[]) {\n    const seen = new Set<string>();\n    for (const line of lines) {\n        const m = /^\\/\\/\\s*@([\\w-]+):/.exec(line);\n        if (m && !seen.has(m[1].toLowerCase())) seen.add(m[1].toLowerCase());\n        else if (m) throw new Error(`Duplicate global option '${m[1]}'`);\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Deduplicate global @options after merging test files.","Remember option keys are matched case-insensitively, so casing differences still collide."],"tags":["fourslash","test-parsing","configuration","duplicate-option"],"backgroundTag":null,"analyzedSha":"b465fdbfe175304d9b977da137b2c178ae1091d3","analyzedAt":"2026-08-12T05:38:42.698Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}