{"record":{"id":"26cf50769c4af578","repo":"ruvnet/RuView","slug":"guidance-limit-must-be-between-1-and-20","errorCode":null,"errorMessage":"guidance limit must be between 1 and 20","messagePattern":"guidance limit must be between 1 and 20","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"harness/homecore/src/guidance.js","lineNumber":82,"sourceCode":"  }\n  if (input.limit !== undefined && (typeof input.limit !== 'number' || !Number.isFinite(input.limit))) {\n    throw new TypeError('guidance limit must be a finite number');\n  }\n  if (options.repoRoot !== undefined && options.repoRoot !== null && typeof options.repoRoot !== 'string') {\n    throw new TypeError('guidance repoRoot must be a string or null');\n  }\n\n  const topic = input.topic === undefined ? 'overview' : input.topic;\n  if (!GUIDANCE_TOPICS.includes(topic)) {\n    throw new RangeError(`unsupported guidance topic: ${topic}`);\n  }\n  const query = input.query === undefined ? '' : input.query.trim();\n  if (query && (query.length < 2 || query.length > 500)) {\n    throw new RangeError('guidance query must contain 2..500 characters');\n  }\n  const rawLimit = input.limit === undefined ? 20 : input.limit;\n  if (rawLimit < 1 || rawLimit > 20) {\n    throw new RangeError('guidance limit must be between 1 and 20');\n  }\n  const limit = Math.floor(rawLimit);\n  const wanted = tokenize(query);\n\n  const candidates = CAPABILITIES\n    .filter((capability) => topic === 'overview' || capability.topics.includes(topic))\n    .map((capability, order) => ({\n      capability,\n      order,\n      score: scoreCapability(capability, wanted),\n    }))\n    .filter(({ score }) => score > 0)\n    .sort((a, b) => b.score - a.score || a.order - b.order)\n    .slice(0, limit)\n    .map(({ capability }) => ({\n      ...capability,\n      topics: [...capability.topics],\n      sources: [...capability.sources],","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/harness/homecore/src/guidance.js#L64-L100","documentation":"The role check inside require_role's decorator (auth.py:406): check_permission returned False, meaning the authenticated user's roles list contains neither the required role nor the 'admin' super-role. The message interpolates the exact missing role, e.g. \"Role 'admin' required\".","triggerScenarios":"A user created with default roles ['user'] calling a @require_role('admin') route; calling a 'sensor' or 'operator' endpoint with an account provisioned without that role; roles claim in the JWT stale because the token predates a role grant.","commonSituations":"Fresh registrations defaulting to ['user'] hitting admin tooling; role granted in the store but old unexpired token still carries the old roles claim; role-name typos between provisioning and the decorator ('Admin' vs 'admin' - check is case-sensitive).","solutions":["Grant the role: update the user record so roles includes the required value, then log in again so the JWT carries it","Check for case/whitespace mismatches between the provisioned role string and required_role","If the role was just granted but the token is old, refresh or re-login to mint a token with current roles"],"exampleFix":"# before\nuser_manager.create_user('alice', 'a@b.c', pw)  # roles default to ['user']\n# after\nuser_manager.create_user('alice', 'a@b.c', pw, roles=['admin'])  # then re-login","handlingStrategy":"type-guard","validationCode":"def user_has_role(user_info: dict, required_role: str) -> bool:\n    \"\"\"Mirrors check_permission including the admin bypass.\"\"\"\n    roles = user_info.get(\"roles\", []) if user_info else []\n    return \"admin\" in roles or required_role in roles","typeGuard":"from typing import Any, Dict\n\ndef can_access(user_info: Any, required_role: str) -> bool:\n    \"\"\"Narrow an optional user dict to 'authorized for required_role'.\"\"\"\n    if not isinstance(user_info, dict):\n        return False\n    roles = user_info.get(\"roles\") or []\n    return isinstance(roles, list) and (\"admin\" in roles or required_role in roles)","tryCatchPattern":"from src.middleware.auth import AuthorizationError\n\ntry:\n    result = await protected_handler(request)\nexcept AuthorizationError as e:\n    if \"required\" in str(e):  # \"Role 'X' required\"\n        raise HTTPException(403, detail=str(e))\n    raise","preventionTips":["Re-login after role grants so the JWT roles claim is current","Use exact lowercase role strings everywhere","Check can_access() client-side to hide unauthorized actions"],"tags":["auth","authorization","rbac"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}