mem0ai/mem0 · warning · Error

At least one of text, metadata, timestamp, or expirationDate

Error message

At least one of text, metadata, timestamp, or expirationDate must be provided for update.

What it means

MemoryClient.update() requires at least one mutable field — text, metadata, timestamp, or expirationDate — and throws if all four are undefined. A memory update with no changes would be a no-op PATCH, so the client rejects it up front with an explicit message naming the accepted fields.

Source

Thrown at mem0-ts/src/client/mem0.ts:360

    {
      text,
      metadata,
      timestamp,
      expirationDate,
    }: {
      text?: string;
      metadata?: Record<string, any>;
      timestamp?: number | string;
      expirationDate?: string | null;
    },
  ): Promise<Array<Memory>> {
    if (
      text === undefined &&
      metadata === undefined &&
      timestamp === undefined &&
      expirationDate === undefined
    ) {
      throw new Error(
        "At least one of text, metadata, timestamp, or expirationDate must be provided for update.",
      );
    }

    const payload: Record<string, any> = {};
    if (text !== undefined) payload.text = text;
    if (metadata !== undefined) payload.metadata = metadata;
    if (timestamp !== undefined) payload.timestamp = timestamp;
    if (expirationDate !== undefined) payload.expiration_date = expirationDate;

    const payloadKeys = Object.keys(payload);
    this._captureEvent("update", [payloadKeys]);

    const response = await this._fetchWithErrorHandling(
      `${this.host}/v1/memories/${encodePathSegment(memoryId)}/`,
      {
        method: "PUT",
        headers: this.headers,

View on GitHub (pinned to 001c235229)

Solutions

  1. Ensure at least one field is set: client.update(id, { text: newText }).
  2. Guard dynamically built payloads: if (!Object.values(patch).some(v => v !== undefined)) return.
  3. Check field spelling — expirationDate (camelCase) maps to expiration_date in the payload; typo'd keys are silently undefined.

Example fix

// before
await client.update(memoryId, {}); // nothing to update

// after
await client.update(memoryId, { text: 'updated content' });
Defensive patterns

Strategy: validation

Validate before calling

type UpdateFields = { text?: string; metadata?: Record<string, unknown>; timestamp?: number | string; expirationDate?: string | null };

function hasAnyUpdateField(p: UpdateFields): boolean {
  return p.text !== undefined || p.metadata !== undefined ||
         p.timestamp !== undefined || p.expirationDate !== undefined;
}

if (!hasAnyUpdateField(patch)) return; // nothing to do — skip the call

Type guard

const isUpdatePatch = (p: unknown): p is { text: string } | { metadata: Record<string, unknown> } | { timestamp: number | string } | { expirationDate: string | null } =>
  typeof p === 'object' && p !== null &&
  ['text', 'metadata', 'timestamp', 'expirationDate'].some(k => (p as Record<string, unknown>)[k] !== undefined);

Prevention

When it happens

Trigger: await client.update(memoryId, {}) — building the update object dynamically and every property ending up undefined (typos like `textt`, conditionals that skip all assignments, or spreading an empty source object).

Common situations: Forms where no field was edited but save was clicked; partial-update builders using conditional spreads that collapse to nothing; property-name mismatches (expiryDate vs expirationDate) leaving the payload empty.

Related errors


AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15). Data as JSON: /api/errors/f7ee7fb61112c936. Report an issue: GitHub.