thedotmack/claude-mem · warning

${operationType}: no user_prompts row for prompt #${promptNu

Error message

${operationType}: no user_prompts row for prompt #${promptNumber} — ingesting anyway (session-init likely raced worker boot; see #2794/#2795)

What it means

PrivacyCheckValidator.checkUserPromptPrivacy looks up the user_prompts row for (contentSessionId, promptNumber, sessionDbId). When store.getUserPrompt returns null it logs this warning and returns {allow: true, prompt: ''} — privacy filtering is bypassed and observation/summarize ingestion proceeds with an empty prompt. This is a known cold-boot race (issues #2794/#2795): the session-init hook had not yet written the row when the worker processed the prompt.

Source

Thrown at src/services/worker/validation/PrivacyCheckValidator.ts:32

   *    never persisted the prompt for this session (e.g. the UserPromptSubmit
   *    hook raced worker boot, #2795). This is NOT a privacy signal — treating
   *    it as "private" silently freezes EVERY observation for the session.
   *    Allow ingestion and emit a visible warn.
   *  - The row is PRESENT but empty after privacy stripping (''/whitespace):
   *    the user genuinely redacted the turn → suppress.
   */
  static checkUserPromptPrivacy(
    store: SessionStore,
    contentSessionId: string,
    promptNumber: number,
    operationType: 'observation' | 'summarize',
    sessionDbId: number,
    additionalContext?: Record<string, any>
  ): PromptPrivacyDecision {
    const userPrompt = store.getUserPrompt(contentSessionId, promptNumber, sessionDbId);

    if (userPrompt === null) {
      logger.warn(
        'HOOK',
        `${operationType}: no user_prompts row for prompt #${promptNumber} — ingesting anyway (session-init likely raced worker boot; see #2794/#2795)`,
        { sessionId: sessionDbId, contentSessionId, promptNumber, ...additionalContext }
      );
      return { allow: true, prompt: '' };
    }

    if (userPrompt.trim() === '') {
      logger.debug('HOOK', `Skipping ${operationType} - user prompt was entirely private`, {
        sessionId: sessionDbId,
        promptNumber,
        ...additionalContext,
      });
      return { allow: false, reason: 'private' };
    }

    return { allow: true, prompt: userPrompt };
  }

View on GitHub (pinned to e2d1df569a)

Solutions

  1. No data-loss action needed — ingestion proceeds and the row usually lands moments later; subsequent prompts find their rows.
  2. If it recurs outside cold boot, check worker logs for session-init hook failures (that hook is the row writer).
  3. Upgrade claude-mem: the ~15s cold-boot wait was added specifically to shrink this window (#2795).
  4. Confirm the session-init hook is registered and executing in Claude Code hook config.
Defensive patterns

Strategy: validation

Validate before calling

const row = store.getUserPrompt(contentSessionId, promptNumber, sessionDbId);
if (row === null) {
  // session-init hook has not landed yet — defer instead of ingesting blind
  return { defer: true };
}

Type guard

const hasUserPromptRow = (
  store: SessionStore, csid: string, n: number, dbId: number
): boolean => store.getUserPrompt(csid, n, dbId) !== null;

Prevention

When it happens

Trigger: An observation or summarize operation calls checkUserPromptPrivacy before the SessionStart/session-init hook has inserted the user_prompts row — typically when the context hook races worker startup on a cold boot (~7s Chroma bind).

Common situations: First prompt immediately after machine reboot; worker restarting mid-session after an upgrade; heavy system load delaying the session-init hook past the observation hook.

Related errors


AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20). Data as JSON: /api/errors/8b5eb9d6330e3d78. Report an issue: GitHub.