{"record":{"id":"ac6981d63a88f1d0","repo":"pnpm/pnpm","slug":"token-helper-unsupported-character","errorCode":"TOKEN_HELPER_UNSUPPORTED_CHARACTER","errorMessage":"Unexpected character ${JSON.stringify(char)}","messagePattern":"Unexpected character (.+?)","errorType":"error_code","errorClass":"TokenHelperUnsupportedCharacterError","httpStatus":null,"severity":"error","filePath":"pnpm11/config/reader/src/parseCreds.ts","lineNumber":142,"sourceCode":"  }\n}\n\n\n/** Characters reserved for more advanced features in the future. */\nconst RESERVED_CHARACTERS = new Set(['$', '%', '`', '\"', \"'\"])\n\n/**\n * Parse a value of `tokenHelper` from an rc file into an array of\n * token helper command and its arguments.\n */\nfunction parseTokenHelper (source: string): TokenHelper {\n  source = source.trim()\n\n  for (const char of source) {\n    // We'll only support a simple syntax for now.\n    // In the future, we may add quotations and environment variable interpolations.\n    if (RESERVED_CHARACTERS.has(char)) {\n      throw new TokenHelperUnsupportedCharacterError(char)\n    }\n  }\n\n  const command = source.split(/\\s+/).filter(Boolean)\n\n  return command as [string, ...string[]]\n}\n\nexport class TokenHelperUnsupportedCharacterError extends PnpmError {\n  readonly char: string\n  constructor (char: string) {\n    let hint = 'Try wrapping the current command in a script whose name does not contain unsupported characters'\n    if (char === '\"' || char === \"'\") {\n      hint = `pnpm does not support quotations in tokenHelper. ${hint}`\n    } else if (char === '$' || char === '%') {\n      hint = `pnpm does not support environment variables. ${hint}`\n    }\n    super('TOKEN_HELPER_UNSUPPORTED_CHARACTER', `Unexpected character ${JSON.stringify(char)}`, { hint })","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/pnpm/pnpm/blob/5b11d3a15b9022a2109cb18ed96a5d652630371f/pnpm11/config/reader/src/parseCreds.ts#L124-L160","documentation":"The `tokenHelper` rc key is parsed with a deliberately minimal grammar: the value is trimmed, split on whitespace into command plus arguments, and nothing else. The characters `$`, `%`, backtick, `\"`, and `'` are reserved for future features (quoting and env interpolation), so any occurrence throws TOKEN_HELPER_UNSUPPORTED_CHARACTER. The error exposes the offending char and a hint specific to quotes vs env vars.","triggerScenarios":"tokenHelper=/bin/sh -c \"cat ~/.npm-token\" (quotes), tokenHelper=./get-token $NPM_TOKEN (POSIX env var), tokenHelper=helper.cmd %TOKEN% (Windows env var), or any backtick command substitution.","commonSituations":"Porting a shell one-liner from another tool's config; trying to pass an argument containing spaces via quotes; CI setups that want the token through an environment variable.","solutions":["Drop the quotes — tokenHelper only accepts plain whitespace-separated tokens","Move quoting, env access, or shell logic into a small wrapper script and set tokenHelper to its path (plus simple args)","Ensure the helper path itself contains no spaces or reserved characters"],"exampleFix":"# before (.npmrc)\ntokenHelper=/bin/sh -c \"cat $HOME/.npm-token\"\n# after — /usr/local/bin/fetch-npm-token is a script doing `cat \"$HOME/.npm-token\"`\ntokenHelper=/usr/local/bin/fetch-npm-token","handlingStrategy":"validation","validationCode":"const TOKEN_HELPER_RESERVED = new Set(['$', '%', '`', '\"', \"'\"])\nfunction isParsableTokenHelper (value: string): boolean {\n  const s = value.trim()\n  return s.length > 0 && Array.from(s).every(ch => !TOKEN_HELPER_RESERVED.has(ch))\n}","typeGuard":null,"tryCatchPattern":"catch (err) {\n  if (err instanceof PnpmError && err.code === 'TOKEN_HELPER_UNSUPPORTED_CHARACTER') {\n    // err.char is the offending character; err.hint suggests a wrapper script\n  } else throw err\n}","preventionTips":["Keep tokenHelper a bare executable path plus simple args","Put quoting and env-var logic in a wrapper script","Document the reserved characters ($ % ` \" ') in team docs"],"tags":["auth","npmrc","shell","token-helper"],"backgroundTag":null,"analyzedSha":"5b11d3a15b9022a2109cb18ed96a5d652630371f","analyzedAt":"2026-08-16T13:18:59.198Z","schemaVersion":2},"datasetVersion":"2026-08-16T18:17:16.020Z"}