ruvnet/RuView · error · Error

duplicate brain id: ${record.id}

Error message

duplicate brain id: ${record.id}

What it means

Raised at auth.py:265 after a token passes signature and expiry verification but its decoded payload has no 'sub' claim (or sub is empty/None). The middleware requires sub to carry the username it will look up in UserManager.

Source

Thrown at harness/homecore/src/brain.js:91

  if (Buffer.byteLength(raw) > 1_048_576) throw new Error('brain corpus exceeds 1 MiB');
  const records = raw.split('\n').filter(Boolean).map((line, index) => {
    if (Buffer.byteLength(line) > 16_384) {
      throw new Error(`brain line ${index + 1}: exceeds 16 KiB`);
    }
    let record;
    try {
      record = JSON.parse(line);
    } catch (error) {
      throw new Error(`brain line ${index + 1}: ${error.message}`);
    }
    const errors = validateBrainRecord(record, { canonical: true });
    if (errors.length) throw new Error(`brain line ${index + 1}: ${errors.join('; ')}`);
    return Object.freeze(record);
  });
  if (records.length > 1000) throw new Error('brain corpus exceeds 1000 records');
  const ids = new Set();
  for (const record of records) {
    if (ids.has(record.id)) throw new Error(`duplicate brain id: ${record.id}`);
    ids.add(record.id);
  }
  return { records, digest: sha256(raw), bytes: Buffer.byteLength(raw) };
}

function terms(value) {
  return new Set(String(value).toLowerCase().match(/[a-z0-9][a-z0-9_-]{1,}/g) || []);
}

export function searchBrain(query, { limit = 8, path = CORPUS_PATH } = {}) {
  const wanted = terms(query);
  if (!wanted.size) return [];
  const { records, digest } = loadBrain(path);
  return records.map((record) => {
    const title = terms(record.title);
    const body = terms(record.content);
    const tags = new Set(record.tags.map((tag) => tag.toLowerCase()));
    let score = 0;

View on GitHub (pinned to 4685618388)

Solutions

  1. Always include 'sub': username when building token_data before create_access_token
  2. If consuming foreign tokens, map their subject claim to sub before verification or extend the lookup to the actual claim name
  3. Validate issued tokens with jwt.io / decode_token_claims to confirm sub is present

Example fix

# before
access_token = token_manager.create_access_token({"email": email})
# after
access_token = token_manager.create_access_token({"sub": username, "email": email})
Defensive patterns

Strategy: validation

Validate before calling

def token_data_is_valid(token_data: dict) -> bool:
    """create_access_token payload must carry a non-empty sub for _authenticate_request."""
    return bool(token_data.get("sub"))

Type guard

def has_subject_claim(claims: dict) -> bool:
    return isinstance(claims.get("sub"), str) and len(claims["sub"]) > 0

Try / catch

try:
    user_info = await middleware._authenticate_request(request)
except AuthenticationError as e:
    if str(e) == "Invalid token payload":
        # token verified but lacks sub -> re-issue with sub at the token mint
        return json_response({"error": "token missing sub claim; re-issue required"}, 401)
    raise

Prevention

When it happens

Trigger: Calling TokenManager.create_access_token({'email': ...}) without a 'sub' key; tokens minted by a different service that uses 'username' or 'user_id' as the subject claim instead of 'sub'; payload.get('sub') returning '' because an empty string was set.

Common situations: Custom token issuance code that forgets the sub claim; integrating third-party-issued JWTs whose claim names differ; test fixtures hand-crafting payloads with only exp/iat.

Related errors


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