ruvnet/RuView · error · RangeError

guidance limit must be between 1 and 20

Error message

guidance limit must be between 1 and 20

What it means

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".

Source

Thrown at harness/homecore/src/guidance.js:82

  }
  if (input.limit !== undefined && (typeof input.limit !== 'number' || !Number.isFinite(input.limit))) {
    throw new TypeError('guidance limit must be a finite number');
  }
  if (options.repoRoot !== undefined && options.repoRoot !== null && typeof options.repoRoot !== 'string') {
    throw new TypeError('guidance repoRoot must be a string or null');
  }

  const topic = input.topic === undefined ? 'overview' : input.topic;
  if (!GUIDANCE_TOPICS.includes(topic)) {
    throw new RangeError(`unsupported guidance topic: ${topic}`);
  }
  const query = input.query === undefined ? '' : input.query.trim();
  if (query && (query.length < 2 || query.length > 500)) {
    throw new RangeError('guidance query must contain 2..500 characters');
  }
  const rawLimit = input.limit === undefined ? 20 : input.limit;
  if (rawLimit < 1 || rawLimit > 20) {
    throw new RangeError('guidance limit must be between 1 and 20');
  }
  const limit = Math.floor(rawLimit);
  const wanted = tokenize(query);

  const candidates = CAPABILITIES
    .filter((capability) => topic === 'overview' || capability.topics.includes(topic))
    .map((capability, order) => ({
      capability,
      order,
      score: scoreCapability(capability, wanted),
    }))
    .filter(({ score }) => score > 0)
    .sort((a, b) => b.score - a.score || a.order - b.order)
    .slice(0, limit)
    .map(({ capability }) => ({
      ...capability,
      topics: [...capability.topics],
      sources: [...capability.sources],

View on GitHub (pinned to 4685618388)

Solutions

  1. Grant the role: update the user record so roles includes the required value, then log in again so the JWT carries it
  2. Check for case/whitespace mismatches between the provisioned role string and required_role
  3. If the role was just granted but the token is old, refresh or re-login to mint a token with current roles

Example fix

# before
user_manager.create_user('alice', 'a@b.c', pw)  # roles default to ['user']
# after
user_manager.create_user('alice', 'a@b.c', pw, roles=['admin'])  # then re-login
Defensive patterns

Strategy: type-guard

Validate before calling

def user_has_role(user_info: dict, required_role: str) -> bool:
    """Mirrors check_permission including the admin bypass."""
    roles = user_info.get("roles", []) if user_info else []
    return "admin" in roles or required_role in roles

Type guard

from typing import Any, Dict

def can_access(user_info: Any, required_role: str) -> bool:
    """Narrow an optional user dict to 'authorized for required_role'."""
    if not isinstance(user_info, dict):
        return False
    roles = user_info.get("roles") or []
    return isinstance(roles, list) and ("admin" in roles or required_role in roles)

Try / catch

from src.middleware.auth import AuthorizationError

try:
    result = await protected_handler(request)
except AuthorizationError as e:
    if "required" in str(e):  # "Role 'X' required"
        raise HTTPException(403, detail=str(e))
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/26cf50769c4af578. Report an issue: GitHub.