{"record":{"id":"87230f8e3d4e9fec","repo":"ruvnet/RuView","slug":"unsupported-guidance-topic-topic","errorCode":null,"errorMessage":"unsupported guidance topic: ${topic}","messagePattern":"unsupported guidance topic: (.+?)","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"harness/homecore/src/guidance.js","lineNumber":74,"sourceCode":"  if (!options || typeof options !== 'object' || Array.isArray(options)) {\n    throw new TypeError('guidance options must be an object');\n  }\n  if (input.topic !== undefined && typeof input.topic !== 'string') {\n    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),","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/harness/homecore/src/guidance.js#L56-L92","documentation":"The blanket 'except Exception' at auth.py:381 wraps the whole refresh_token body, converting every failure - including the inner AuthenticationError('User not found or inactive') raised a few lines above, since AuthenticationError subclasses Exception - into 'Token refresh failed'. The true cause is discarded (not even logged here), which makes diagnosis misleading.","triggerScenarios":"Refreshing an expired or invalid token (inner verify_token raises AuthenticationError('Invalid token'), then masked); refreshing for a missing/inactive user ('User not found or inactive' masked); create_access_token failing because settings.jwt_expire_hours is None and the timedelta arithmetic raises TypeError.","commonSituations":"Client refresh racing token expiry: expired token produces 'Token refresh failed' instead of a clearer invalid-token message; misconfigured settings surfacing as opaque auth errors; debugging wasted because the inner message never reaches logs.","solutions":["Treat any 'Token refresh failed' as 're-login required': call login to get a fresh token","Patch the handler to re-raise AuthenticationError before the generic except, and log the original exception: except AuthenticationError: raise / except Exception as e: logger.error(e)","Verify settings.jwt_expire_hours and jwt_algorithm are set so token creation inside refresh cannot blow up"],"exampleFix":"# before (auth.py:381)\nexcept Exception as e:\n    raise AuthenticationError(\"Token refresh failed\")\n# after\nexcept AuthenticationError:\n    raise\nexcept Exception as e:\n    logger.error(f\"Token refresh error: {e}\")\n    raise AuthenticationError(\"Token refresh failed\")","handlingStrategy":"fallback","validationCode":"def refresh_preconditions_ok(settings) -> bool:\n    \"\"\"create_access_token inside refresh needs these settings present.\"\"\"\n    return (\n        getattr(settings, \"jwt_expire_hours\", None) is not None\n        and bool(getattr(settings, \"secret_key\", None))\n        and bool(getattr(settings, \"jwt_algorithm\", None))\n    )","typeGuard":null,"tryCatchPattern":"try:\n    tokens = await middleware.refresh_token(token)\nexcept AuthenticationError as e:\n    if str(e) == \"Token refresh failed\":\n        # opaque mask: any cause -> fall back to full login\n        tokens = await middleware.login(username, password)\n    else:\n        raise","preventionTips":["Patch the handler to re-raise AuthenticationError before the generic except","Log the original exception in the catch-all","Clients: treat refresh failure as login-required, never retry the same token"],"tags":["auth","refresh-token","error-masking"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}