{"record":{"id":"20e2533f4d122e78","repo":"ruvnet/RuView","slug":"guidance-query-must-contain-2-500-characters","errorCode":null,"errorMessage":"guidance query must contain 2..500 characters","messagePattern":"guidance query must contain 2\\.\\.500 characters","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"harness/homecore/src/guidance.js","lineNumber":78,"sourceCode":"    throw new TypeError('guidance topic must be a string');\n  }\n  if (input.query !== undefined && typeof input.query !== 'string') {\n    throw new TypeError('guidance query must be a string');\n  }\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)","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/harness/homecore/src/guidance.js#L60-L96","documentation":"require_role(...)'s decorator wrapper (auth.py:403) raises AuthorizationError('Authentication required') when request.state.user is None - i.e. the auth middleware never attached a user to this request. This is an ordering/wiring error, not a credentials error: the route is decorated for roles but runs before/untogether with AuthenticationMiddleware.","triggerScenarios":"Applying @require_role('admin') to a route while AuthenticationMiddleware is not installed (get_auth_middleware never added to app); middleware ordering puts the route handler before auth runs; the request path is excluded from auth (not /api/* or /ws/*) yet the decorator still expects a user.","commonSituations":"Adding role-guards to routes in a new app instance where auth middleware was forgotten; testing route handlers directly (TestClient without middleware); paths outside /api/ that skip auth by design but are decorated anyway.","solutions":["Install the middleware: app.add_middleware(...) with get_auth_middleware(settings) so request.state.user gets populated","Keep role-protected routes under /api/ so _requires_auth triggers authentication","Alternatively use the FastAPI dependency require_role(role) (auth.py:445) which raises a proper 401 itself"],"exampleFix":"# before\n@app.get('/admin/stats')\n@auth_middleware.require_role('admin')\nasync def stats(request: Request): ...\n# middleware never added\n# after\napp.add_middleware(AuthenticationMiddleware, ...)  # or get_auth_middleware(settings) wiring\n@app.get('/api/admin/stats')\n@auth_middleware.require_role('admin')\nasync def stats(request: Request): ...","handlingStrategy":"validation","validationCode":"def middleware_wired(app) -> bool:\n    \"\"\"require_role's decorator reads request.state.user, set only by the middleware.\"\"\"\n    return any(\"AuthenticationMiddleware\" in str(m.cls) for m in getattr(app, \"user_middleware\", []))","typeGuard":"def request_has_user(request) -> bool:\n    return getattr(getattr(request, \"state\", None), \"user\", None) is not None","tryCatchPattern":"from src.middleware.auth import AuthorizationError\n\ntry:\n    return await func(request, *args, **kwargs)\nexcept AuthorizationError as e:\n    if str(e) == \"Authentication required\":\n        raise HTTPException(401, detail=str(e), headers={\"WWW-Authenticate\": \"Bearer\"})\n    raise HTTPException(403, detail=str(e))","preventionTips":["Always install AuthenticationMiddleware when using the decorator","Prefer the dependency form require_role() which fails with clean HTTP errors","Mount protected routes under /api/ so auth actually runs"],"tags":["auth","authorization","middleware","fastapi"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}