{"record":{"id":"e377e023cd6bc2e5","repo":"garrytan/gstack","slug":"tab-each-rejected-subcommand-innerraw-not-al","errorCode":null,"errorMessage":"tab-each rejected: subcommand \"${innerRaw}\" not allowed by your token scope (${tokenInfo.scopes.join(', ')}).","messagePattern":"tab-each rejected: subcommand \"(.+?)\" not allowed by your token scope \\((.+?)\\)\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/meta-commands.ts","lineNumber":328,"sourceCode":"      //   $B tab-each snapshot -i      → snapshot every tab\n      //   $B tab-each text             → grab clean text from every tab\n      //   $B tab-each goto https://x.y → load the same URL in every tab\n      if (args.length === 0) {\n        throw new Error(\n          'Usage: browse tab-each <command> [args...]\\n' +\n          'Example: browse tab-each snapshot -i'\n        );\n      }\n\n      const innerRaw = args[0];\n      const innerName = canonicalizeCommand(innerRaw);\n      const innerArgs = args.slice(1);\n\n      // Scope check the inner command before fanning out, so a single\n      // permission failure aborts the whole batch instead of partially\n      // mutating tabs.\n      if (tokenInfo && tokenInfo.clientId !== 'root' && !checkScope(tokenInfo, innerName)) {\n        throw new Error(\n          `tab-each rejected: subcommand \"${innerRaw}\" not allowed by your token scope (${tokenInfo.scopes.join(', ')}).`\n        );\n      }\n\n      const tabs = await bm.getTabListWithTitles();\n      const originalActive = tabs.find(t => t.active)?.id ?? bm.getActiveTabId();\n\n      const executeCmd = opts?.executeCommand;\n      const results: Array<{\n        tabId: number;\n        url: string;\n        title: string;\n        status: number;\n        output: string;\n      }> = [];\n\n      try {\n        for (const tab of tabs) {","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/meta-commands.ts#L310-L346","documentation":"Thrown by tab-each when the caller's token does not have the required scope for the inner command. Before fanning out, tab-each canonicalizes the inner command name and runs checkScope(tokenInfo, innerName). Non-root tokens whose scopes do not include the inner command are rejected to prevent a single permission failure from partially mutating tabs — the scope check is done upfront so the whole batch aborts atomically.","triggerScenarios":"Calling 'browse tab-each <cmd>' with a scoped (non-root) token whose scopes array does not include the canonicalized inner command name.","commonSituations":"Using a limited-scope API token (e.g., read-only) and trying to fan out a write command like 'goto' or 'click'. Or the token was created with specific scopes that exclude the desired inner command.","solutions":["Use a token whose scopes include the inner command — check tokenInfo.scopes for the missing permission","Switch to a root token (clientId === 'root') for operations requiring elevated scope","Request the additional scope be added to the token by an administrator"],"exampleFix":"# before — read-only token trying a write command\n$B tab-each goto https://example.com  # token scope: snapshot,text\n\n# after — use a token with goto scope\n# or use root token","handlingStrategy":"validation","validationCode":"// Verify scope before fanning out\nconst innerName = canonicalizeCommand(args[0]);\nif (tokenInfo && tokenInfo.clientId !== 'root' && !checkScope(tokenInfo, innerName)) {\n  throw new Error(`Token lacks scope for '${innerName}'. Available: ${tokenInfo.scopes.join(', ')}`);\n}","typeGuard":"function tokenHasScope(token: TokenInfo, cmd: string): boolean {\n  return token.clientId === 'root' || checkScope(token, cmd);\n}","tryCatchPattern":null,"preventionTips":["Check tokenInfo.scoes before attempting tab-each with a non-root token","Use a root token for operations requiring broad permissions","Request additional scopes from an administrator if needed"],"tags":["browse","security","authorization","tabs","scope"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}