{"record":{"id":"2debc0c135c9267f","repo":"denoland/deno","slug":"err-invalid-char-2debc0","errorCode":"ERR_INVALID_CHAR","errorMessage":"Invalid character in alt","messagePattern":"Invalid character in alt","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/http2.ts","lineNumber":4621,"sourceCode":"      // than a URL, then it is possible that origin will be malformed.\n      // We do not verify that here. Users who go that route need to\n      // ensure they are doing the right thing or the payload data will\n      // be invalid.\n      if (typeof origin !== \"string\") {\n        throw new ERR_INVALID_ARG_TYPE(\"originOrStream\", [\n          \"string\",\n          \"number\",\n          \"URL\",\n          \"object\",\n        ], originOrStream);\n      } else if (origin === \"null\" || origin.length === 0) {\n        throw new ERR_HTTP2_ALTSVC_INVALID_ORIGIN();\n      }\n    }\n\n    validateString(alt, \"alt\");\n    if (!kQuotedString.test(alt)) {\n      throw new ERR_INVALID_CHAR(\"alt\");\n    }\n\n    // Max length permitted for ALTSVC\n    if (\n      (alt.length + (origin !== undefined ? origin.length : 0)) > kMaxALTSVC\n    ) {\n      throw new ERR_HTTP2_ALTSVC_LENGTH();\n    }\n\n    this[kHandle].altsvc(stream, origin || \"\", alt);\n  }\n\n  // Submits an origin frame to be sent.\n  origin(...origins) {\n    if (this.destroyed) {\n      throw new ERR_HTTP2_INVALID_SESSION();\n    }\n","sourceCodeStart":4603,"sourceCodeEnd":4639,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/http2.ts#L4603-L4639","documentation":"altsvc()'s alt parameter is validated against kQuotedString, which in this file is the regex ^[\\x09\\x20-\\x5b\\x5d-\\x7e\\x80-\\xff]*$ — tab, printable ASCII (excluding backslash 0x5c), and bytes 0x80-0xff. Any other byte, notably CR, LF, other control characters, or a backslash, throws ERR_INVALID_CHAR because the ALTSVC field value must be a single-line quoted-string per RFC 7838.","triggerScenarios":"altsvc('h2=\":443\"\\r\\nX: 1', origin) (header-injection attempt or embedded newline); altsvc with a backslash-escaped value; a value containing a NUL or other control character copied from binary data.","commonSituations":"Building the alt value by string concatenation with unsanitized user input; values read from a config file that contains a trailing newline; template literals that interpolate multi-line strings.","solutions":["Keep alt a single-line value such as 'h2=\":443\"' or 'h2-16=\"alt.example.com:443\"'","Strip/validate with alt.trim() plus a control-character check before calling","Treat embedded CR/LF in alt as an injection attempt and reject the input, not just clean it"],"exampleFix":"// before\nsession.altsvc(config.altSvcValue, origin); // value has trailing '\\n'\n\n// after\nconst alt = config.altSvcValue.trim();\nif (/[\\r\\n\\\\]/.test(alt)) throw new Error('invalid alt-svc value');\nsession.altsvc(alt, origin);","handlingStrategy":"validation","validationCode":"if (/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f\\\\]/.test(alt)) {\n  throw new TypeError('alt must be a single-line quoted-string value');\n}\nsession.altsvc(alt, origin);","typeGuard":"function isValidAltValue(alt) {\n  return typeof alt === 'string' && /^[\\t\\x20-\\x5b\\x5d-\\x7e\\x80-\\xff]*$/.test(alt);\n}","tryCatchPattern":null,"preventionTips":["Treat CR/LF in alt as header injection — reject the input at your trust boundary","Trim values loaded from config files to drop trailing newlines","Standard values look like 'h2=\":443\"'; keep them short and literal"],"tags":["http2","node-compat","altsvc","header-injection","argument-validation"],"backgroundTag":"invalid-header-value","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}