thedotmack/claude-mem · error

observation_add: "content" is required

Error message

observation_add: "content" is required

What it means

Validation error inside the observation_add tool handler. After requireServerForObservationTool() passes, the handler checks that args.content is a non-empty trimmed string and throws this plain Error if not. It is an argument-shape guard that runs before the request reaches the server client, so it never causes a network call.

Source

Thrown at src/servers/mcp-server.ts:243

      const err = error instanceof Error ? error : new Error(String(error));
      logger.warn('SYSTEM', `${toolName} failed`, undefined, err);
      return formatToolError(error);
    }
  };
}

interface ObservationAddArgs {
  projectId?: string;
  serverSessionId?: string | null;
  kind?: string;
  content: string;
  metadata?: Record<string, unknown>;
}

const handleObservationAdd = wrapHandler('observation_add', async (args: ObservationAddArgs) => {
  const ctx = requireServerForObservationTool('observation_add');
  if (typeof args?.content !== 'string' || args.content.trim().length === 0) {
    throw new Error('observation_add: "content" is required');
  }
  const projectId = args.projectId && args.projectId.trim().length > 0 ? args.projectId : ctx.projectId;
  const request: ServerAddObservationRequest = {
    projectId,
    content: args.content,
    ...(args.serverSessionId !== undefined ? { serverSessionId: args.serverSessionId } : {}),
    ...(args.kind !== undefined ? { kind: args.kind } : {}),
    ...(args.metadata !== undefined ? { metadata: args.metadata } : {}),
  };
  const response = await ctx.client.addObservation(request);
  return formatJsonResult(response);
});

interface ObservationRecordEventArgs {
  projectId?: string;
  serverSessionId?: string | null;
  contentSessionId?: string | null;
  memorySessionId?: string | null;

View on GitHub (pinned to d768ba3643)

Solutions

  1. Provide a non-empty content string in the tool call arguments.
  2. If you meant to record a typed event rather than free text, use observation_record_event with eventType instead.
  3. Add a client-side check that trims content before calling the tool so the error never fires.

Example fix

// before
await tools.observation_add({ kind: 'note', metadata: { x: 1 } });
// throws 'observation_add: "content" is required'

// after
await tools.observation_add({
  content: 'Refactored ModeManager.loadMode to fall back to code mode.',
  kind: 'note',
});
Defensive patterns

Strategy: validation

Validate before calling

function validObservationContent(args: unknown): args is { content: string } {
  return typeof (args as any)?.content === 'string' && (args as any).content.trim().length > 0;
}
if (!validObservationContent(args)) {
  return { content: [{ type: 'text', text: 'content is required' }], isError: true };
}

Type guard

function isObservationAddArgs(v: unknown): v is { content: string; kind?: string; metadata?: Record<string, unknown> } {
  return typeof (v as any)?.content === 'string' && (v as any).content.trim().length > 0;
}

Prevention

When it happens

Trigger: Calling observation_add with content omitted, null, a non-string, or whitespace-only. The MCP schema may mark content as required but JSON-RPC clients can still send malformed arguments (extraProperties is allowed), so this is the last line of defense.

Common situations: LLM-driven MCP client omits content because it only passed metadata; an automation sends { kind: 'note' } with no content; content is an empty string from a templating bug; caller confused observation_add (free text) with observation_record_event (eventType).

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/769e7fa0701ea08d. Report an issue: GitHub.