mastra-ai/mastra · error · Error

Harness pending item "${item.id}" already exists on session

Error message

Harness pending item "${item.id}" already exists on session "${sessionId}"

What it means

`appendPendingItem` throws this when the target session already contains a pending item with the same `item.id`. The library enforces unique pending-item ids per session to avoid duplicate work items.

Source

Thrown at packages/core/src/storage/domains/harness/base.ts:42

    const next: SessionRecord = {
      ...record,
      ...updates,
      id: record.id,
      createdAt: record.createdAt,
      lastActivityAt: updates.lastActivityAt ?? new Date(),
    };
    await this.saveSession(next);
    return next;
  }

  async appendPendingItem(sessionId: string, item: HarnessPendingItemRecord): Promise<SessionRecord> {
    const record = await this.loadSession(sessionId);
    if (!record) {
      throw new Error(`Harness session "${sessionId}" was not found`);
    }

    if (record.pending?.some(existing => existing.id === item.id)) {
      throw new Error(`Harness pending item "${item.id}" already exists on session "${sessionId}"`);
    }

    return this.updateSession(sessionId, {
      pending: [...(record.pending ?? []), item],
    });
  }

  async updatePendingItem(
    sessionId: string,
    pendingItemId: string,
    updates: Partial<Omit<HarnessPendingItemRecord, 'id' | 'sessionId' | 'createdAt'>>,
  ): Promise<SessionRecord> {
    const record = await this.loadSession(sessionId);
    if (!record) {
      throw new Error(`Harness session "${sessionId}" was not found`);
    }

    let found = false;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Generate a unique id per pending item (crypto.randomUUID() or an incrementing per-session counter).
  2. Check `record.pending` (via `loadSession`) for an existing item with the id before appending.
  3. Switch to `updatePendingItem` when the intent is to modify an existing item rather than add a new one.
  4. Make retry wrappers idempotent: catch this error and treat the append as already-done if the existing item matches.

Example fix

// before
await storage.appendPendingItem(sessionId, { id: 'tool-call', /* ... */ }); // throws on retry
// after
await storage.appendPendingItem(sessionId, { id: crypto.randomUUID(), /* ... */ });
Defensive patterns

Strategy: validation

Validate before calling

const record = await storage.loadSession(sessionId);
if (record?.pending?.some(i => i.id === item.id)) {
  return; // already appended — idempotent no-op
}
await storage.appendPendingItem(sessionId, item);

Type guard

function isPendingItem(x: unknown): x is HarnessPendingItemRecord {
  return typeof x === 'object' && x !== null && typeof (x as any).id === 'string';
}

Try / catch

try {
  await storage.appendPendingItem(sessionId, item);
} catch (e) {
  if (e instanceof Error && e.message.includes('already exists')) {
    return; // treat as success for idempotent retries
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `appendPendingItem(sessionId, item)` where `record.pending` already contains an entry whose `id === item.id` — typically retrying an append after a timeout, or a client generating non-unique ids.

Common situations: Idempotent retry logic that re-sends the same item without checking; crash-recovery replay of a message queue; code that constructs item ids from non-unique fields (timestamps at second granularity, fixed strings like 'plan').

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/5fe3557f35aa04e3. Report an issue: GitHub.