mastra-ai/mastra · error · MastraError

DATASET_ITEM_EXTERNAL_ID_INVALID

DATASET_ITEM_EXTERNAL_ID_INVALID

Error message

Dataset item externalId must be a non-empty string.

What it means

validateDatasetItemExternalId() (a MastraError with id DATASET_ITEM_EXTERNAL_ID_INVALID, domain STORAGE, category USER) rejects an items' externalId of exactly '' (empty string). Undefined/absent externalId is allowed (auto-generated id is used), but an explicit empty string is treated as a caller bug since externalIds are identity keys.

Source

Thrown at packages/core/src/storage/domains/datasets/identity.ts:132

      } satisfies DatasetItemRow;
      if (!datasetItemPayloadsEqual(item, acceptedRow)) {
        conflicts.push({ index, externalId: item.externalId, existingItemId: local.id, reason: 'payload_mismatch' });
      }
      resolvedIds.push(local.id);
      continue;
    }
    const insert = { id: createId(), item };
    inserts.push(insert);
    requestLocal.set(item.externalId, insert);
    resolvedIds.push(insert.id);
  }
  if (conflicts.length) throw createDatasetItemIdentityConflictError(conflicts);
  return { inserts, resolvedIds, existingCurrentItems };
}

export function validateDatasetItemExternalId(externalId: string | undefined): void {
  if (externalId === '') {
    throw new MastraError({
      id: 'DATASET_ITEM_EXTERNAL_ID_INVALID',
      text: 'Dataset item externalId must be a non-empty string.',
      domain: 'STORAGE',
      category: 'USER',
    });
  }
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Omit externalId entirely when you have none, so the library generates an id.
  2. Generate a real externalId before inserting (e.g. crypto.randomUUID() or a domain key).
  3. Validate/coerce input upstream: trim and reject blank keys before building the batch.
  4. Note that undefined passes but '' throws — normalize '' to undefined, never to a placeholder string.

Example fix

// before
await storage.datasets.addItems({ datasetId: 'ds', items: [{ externalId: row.key || '', input: row.data }] });
// after
const externalId = row.key?.trim() || undefined; // undefined -> generated id
await storage.datasets.addItems({ datasetId: 'ds', items: [{ externalId, input: row.data }] });
Defensive patterns

Strategy: validation

Validate before calling

for (const item of items) {
  if (item.externalId === '') throw new Error(`items[${items.indexOf(item)}].externalId must be non-empty or omitted`);
}

Type guard

function hasValidExternalId(item: { externalId?: string }): boolean {
  return item.externalId === undefined || item.externalId.length > 0;
}

Try / catch

try {
  await storage.datasets.addItems({ datasetId, items });
} catch (e) {
  if (e?.id === 'DATASET_ITEM_EXTERNAL_ID_INVALID') {
    // report the offending payload to the caller / fix upstream input
  } else throw e;
}

Prevention

When it happens

Trigger: addItems()/batchInsertItems() with an item like {externalId: '', input, ...} — typically externalId sourced from an empty form field, empty request property, or `String(value)` of null/'' upstream.

Common situations: Importing rows from CSV/spreadsheets where a key column is blank, copying user input straight into externalId, or template code that defaults `externalId: something || ''`.

Related errors


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