ruvnet/RuView · error · TypeError

guidance query must be a string

Error message

guidance query must be a string

What it means

Raised by AuthenticationMiddleware.login (auth.py:305) when UserManager.authenticate_user returns None. That happens for three distinct causes that are deliberately indistinguishable to callers: unknown username, bcrypt password verification failure, or the user being inactive (is_active falsy).

Source

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

  return [...new Set(values)];
}

export function listGuidanceTopics() {
  return GUIDANCE_TOPICS.map((topic) => ({ topic, summary: TOPIC_SUMMARIES[topic] }));
}

export function getGuidance(input = {}, options = {}) {
  if (!input || typeof input !== 'object' || Array.isArray(input)) {
    throw new TypeError('guidance input must be an object');
  }
  if (!options || typeof options !== 'object' || Array.isArray(options)) {
    throw new TypeError('guidance options must be an object');
  }
  if (input.topic !== undefined && typeof input.topic !== 'string') {
    throw new TypeError('guidance topic must be a string');
  }
  if (input.query !== undefined && typeof input.query !== 'string') {
    throw new TypeError('guidance query must be a string');
  }
  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) {

View on GitHub (pinned to 4685618388)

Solutions

  1. Confirm the user exists in this process: user_manager.get_user(name) is not None
  2. Re-register the user if the store was wiped by a restart
  3. Verify the password matches the one used at create_user and check is_active is True
  4. Strip whitespace from form inputs before submitting

Example fix

# before
resp = await middleware.login("alice", "hunter2")
# after
if user_manager.get_user("alice") is None:
    user_manager.create_user("alice", "a@b.c", "hunter2")
resp = await middleware.login("alice", "hunter2")
Defensive patterns

Strategy: try-catch

Validate before calling

def login_should_succeed(user_manager, username: str, password: str) -> bool:
    """Mirrors authenticate_user's three checks without calling login()."""
    user = user_manager.get_user(username)
    if user is None:
        return False
    from src.middleware.auth import pwd_context
    return pwd_context.verify(password, user["hashed_password"]) and bool(user.get("is_active", False))

Try / catch

from src.middleware.auth import AuthenticationError

try:
    result = await middleware.login(username, password)
except AuthenticationError as e:
    if str(e) == "Invalid username or password":
        # unknown user, wrong password, or inactive -- all identical by design
        raise HTTPException(401, "Invalid username or password")
    raise

Prevention

When it happens

Trigger: POST login with a username not in self._users (fresh process, never registered); wrong password; user exists with correct password but is_active=False; typo'd or URL-encoded credentials.

Common situations: Server restarted and in-memory users vanished, so every login fails with this message; user registered on a different instance; password changed elsewhere; copy-paste including whitespace in username/password.

Related errors


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