anything-llm icon indicating copy to clipboard operation
anything-llm copied to clipboard

[BUG]: API Key Authentication Not Associating User with Chat Messages

Open help4bis opened this issue 2 months ago • 0 comments

How are you running AnythingLLM?

Docker (local)

What happened?

API Key Authentication Not Associating User with Chat Messages

Summary

When using API key authentication (Bearer token) with the /api/v1/workspace/:slug/chat endpoint, chat messages are saved with user_id = NULL in the database. This causes crashes when chat history grows beyond the context window limit and the message compression logic tries to access the user property.

Environment

  • AnythingLLM Version: latest (Docker image mintplexlabs/anythingllm:latest)
  • Deployment: Docker
  • Authentication Method: API Key (Bearer token)
  • LLM Provider: Ollama
  • Vector DB: LanceDB

Bug Description

Error Message

TypeError: Cannot read properties of undefined (reading 'user')
    at messageArrayCompressor (/app/server/utils/helpers/chat/index.js:67:35)
    at OllamaAILLM.compressMessages (/app/server/utils/AiProviders/ollama/index.js:408:18)
    at Object.chatSync (/app/server/utils/chats/apiChatHandler.js:295:39)
    at async /app/server/endpoints/api/workspace/index.js:681:24

When It Occurs

  1. API calls work fine initially (first ~5-10 messages)
  2. When chat history exceeds context window limit (~4096 tokens for llama3.2-vision)
  3. AnythingLLM attempts to compress message history
  4. Compression logic crashes because user_id is NULL for API-created messages

Database Evidence

-- Messages created via API have NULL user_id
SELECT id, user_id, LENGTH(prompt) FROM workspace_chats ORDER BY id DESC LIMIT 5;

-- Results:
214 | NULL | 33
213 | 1    | 3221  (created via UI)
212 | 1    | 15    (created via UI)
211 | NULL | 25
210 | NULL | 3115

Root Cause

There are two bugs preventing API key authentication from associating users with chat messages:

Bug 1: validApiKey Middleware

File: /app/server/utils/middleware/validApiKey.js

The middleware validates the API key but doesn't load the associated user into response.locals.user:

async function validApiKey(request, response, next) {
  const multiUserMode = await SystemSettings.isMultiUserMode();
  response.locals.multiUserMode = multiUserMode;

  const auth = request.header("Authorization");
  const bearerKey = auth ? auth.split(" ")[1] : null;
  if (!bearerKey) {
    response.status(403).json({
      error: "No valid api key found.",
    });
    return;
  }

  if (!(await ApiKey.get({ secret: bearerKey }))) {
    response.status(403).json({
      error: "No valid api key found.",
    });
    return;
  }

  // BUG: User is not loaded here!
  next();
}

Bug 2: Workspace Endpoint Hardcoded user: null

File: /app/server/endpoints/api/workspace/index.js (lines ~685 and ~838)

The workspace chat endpoint hardcodes user: null instead of using the authenticated user:

const result = await ApiChatHandler.chatSync({
  workspace,
  message,
  mode,
  user: null,  // BUG: Should be response.locals.user
  thread: null,
  sessionId: !!sessionId ? String(sessionId) : null,
  attachments,
  reset,
});

Proposed Fix

Fix 1: Update validApiKey.js

const { ApiKey } = require("../../models/apiKeys");
const { SystemSettings } = require("../../models/systemSettings");
const { User } = require("../../models/user");

async function validApiKey(request, response, next) {
  const multiUserMode = await SystemSettings.isMultiUserMode();
  response.locals.multiUserMode = multiUserMode;

  const auth = request.header("Authorization");
  const bearerKey = auth ? auth.split(" ")[1] : null;
  if (!bearerKey) {
    response.status(403).json({
      error: "No valid api key found.",
    });
    return;
  }

  // Get the API key from database
  const apiKey = await ApiKey.get({ secret: bearerKey });
  if (!apiKey) {
    response.status(403).json({
      error: "No valid api key found.",
    });
    return;
  }

  // FIX: Load the user who created this API key
  if (apiKey.createdBy) {
    const user = await User.get({ id: apiKey.createdBy });
    if (user) {
      response.locals.user = user;
    }
  }

  next();
}

Fix 2: Update workspace/index.js

Change line ~685:

// Before:
user: null,

// After:
user: response.locals.user || null,

Also change line ~838 (similar pattern in different endpoint).

Impact

Severity: High

  • Breaks long conversations via API
  • Affects all API integrations (WordPress plugins, custom apps, etc.)
  • No workaround without modifying source code
  • Error only appears after several messages, making it hard to debug

Affected Users:

  • Anyone using API key authentication for programmatic access
  • WordPress plugins integrating AnythingLLM
  • Custom applications using the REST API

Workaround

Until fixed, manually update the database after each API call:

UPDATE workspace_chats SET user_id = 1 WHERE user_id IS NULL;

Or apply the code fixes manually after each container update.

Additional Context

  • API key table has createdBy field linking to user ID
  • UI-based authentication works correctly
  • Session-based auth may have similar issue (untested)
  • The api_keys table structure supports user association:
CREATE TABLE "api_keys" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "secret" TEXT,
    "createdBy" INTEGER,  -- Links to users.id
    "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

Test Case

After applying the fix, verify:

  1. API calls properly set user_id in database
  2. Long conversations don't crash
  3. Message compression works correctly
  4. No regression in UI-based chat
# Should succeed without errors
for i in {1..20}; do
  curl -X POST http://localhost:3001/api/v1/workspace/test/chat \
    -H "Authorization: Bearer $API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"message\": \"Message $i\", \"mode\": \"chat\"}"
  sleep 1
done

Related Files

  • /app/server/utils/middleware/validApiKey.js
  • /app/server/endpoints/api/workspace/index.js
  • /app/server/utils/chats/apiChatHandler.js
  • /app/server/utils/helpers/chat/index.js (where error occurs)

Tested Fix: I've applied these changes to my local instance and confirmed:

  • ✅ API calls now properly associate with users
  • ✅ Long conversations work without crashes
  • ✅ Message compression functions correctly
  • ✅ No impact on UI-based authentication

Are there known steps to reproduce?

Steps to Reproduce

  1. Create an API key in AnythingLLM UI
  2. Send API request with Bearer token:
curl -X POST http://localhost:3001/api/v1/workspace/rds-ink/chat \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Test message 1", "mode": "chat"}'
  1. Repeat 10+ times to build up chat history
  2. Send a long prompt that requires history compression
  3. Expected: Chat response with sources
  4. Actual: Error: Cannot read properties of undefined (reading 'user')

help4bis avatar Oct 31 '25 00:10 help4bis