{"record":{"id":"281bee825e043937","repo":"denoland/deno","slug":"notsupportederror-281bee","errorCode":"NotSupportedError","errorMessage":"'name' must not start with '-'","messagePattern":"'name' must not start with '-'","errorType":"exception","errorClass":"DOMException","httpStatus":null,"severity":"error","filePath":"ext/web/locks.js","lineNumber":124,"sourceCode":"    if (arguments.length === 2) {\n      options = {};\n      callback = optionsOrCallback;\n    } else {\n      options = optionsOrCallback;\n    }\n\n    if (typeof callback !== \"function\") {\n      throw new TypeError(\"callback must be a function\");\n    }\n\n    options = webidl.converters.LockOptions(\n      options,\n      prefix,\n      \"Argument 2\",\n    );\n\n    if (StringPrototypeStartsWith(name, \"-\")) {\n      throw new DOMException(\n        \"'name' must not start with '-'\",\n        \"NotSupportedError\",\n      );\n    }\n    if (options.steal && options.ifAvailable) {\n      throw new DOMException(\n        \"'steal' and 'ifAvailable' are exclusive\",\n        \"NotSupportedError\",\n      );\n    }\n    if (options.steal && options.mode !== \"exclusive\") {\n      throw new DOMException(\n        \"'mode' must be 'exclusive' if 'steal' is specified\",\n        \"NotSupportedError\",\n      );\n    }\n    if (options.signal && (options.steal || options.ifAvailable)) {\n      throw new DOMException(","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/web/locks.js#L106-L142","documentation":"The Web Locks API reserves lock names beginning with a hyphen ('-') for internal use, so LockManager.request rejects them with a DOMException named NotSupportedError, thrown from ext/web/locks.js after option conversion but before acquiring anything. The check uses startsWith on the raw name string. Nothing was locked when this throws.","triggerScenarios":"navigator.locks.request(\"-mylock\", cb); names built from user input or generated IDs that start with '-'; prefixing names with '-' as a namespace convention ('-app:resource').","commonSituations":"Auto-generated lock keys from slugs or CLI-style flags ('--job-42'); copying a naming scheme from a queue/redis library where '-' prefixes are allowed; concatenating '-' separators after an empty first segment, producing a leading '-'.","solutions":["Rename the lock so it does not start with '-', e.g. 'mylock' or 'app:mylock'.","If names derive from user input, sanitize: name.replace(/^-+/, \"\").","Validate generated names with a check like name.startsWith('-') before calling request.","Pick a namespace separator that cannot appear at position 0, such as ':' or '__'."],"exampleFix":"// before\nnavigator.locks.request(\"-job:\" + id, cb);\n\n// after\nnavigator.locks.request(\"job:\" + id, cb);","handlingStrategy":"validation","validationCode":"function lockName(name) {\n  const n = name.replace(/^-+/, \"\");\n  if (n.startsWith(\"-\")) throw new Error(`invalid lock name: ${name}`);\n  return n;\n}\nnavigator.locks.request(lockName(raw), cb);","typeGuard":"function isValidLockName(name) {\n  return typeof name === \"string\" && name.length > 0 && !name.startsWith(\"-\");\n}","tryCatchPattern":"try { await navigator.locks.request(name, cb); } catch (e) { if (e.name === \"NotSupportedError\" && e.message.includes(\"'-'\")) await navigator.locks.request(name.replace(/^-+/, \"\"), cb); else throw e; }","preventionTips":["Pick a lock-name scheme that cannot start with '-' (e.g. 'app:resource').","Sanitize user-derived names at the system boundary.","Add a unit test for the naming helper covering leading hyphens."],"tags":["web-locks","locks","web-api","domexception","naming"],"backgroundTag":"reserved-name-prefix","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}