## TL;DR

NetSuite enforces external ID uniqueness on vendor bills, so a retry after a partial failure collides with the already-created bill. Make imports idempotent: check for an existing bill with the external ID before creating, and on duplicate-key errors, fetch the existing bill instead of failing. Never generate a new external ID on retry.

## Error

```text
Multiple rows in your primary CSV file have the same value for the key column. Please edit this file to eliminate rows with duplicate key column values for field 'ExternalId'
```

## Steps

1. Search for an existing vendor bill with the external ID.
   Expected: The already-created bill, if any.
2. If found, treat the import as already done and reconcile fields.
   Expected: Idempotent behavior.
3. If not found, check for duplicate rows within your own import file.
   Expected: The true duplicate.
4. Dedupe the file and re-run.
   Expected: A clean import.
5. Record import outcomes per external ID so retries are safe.
   Expected: No double-posting ever.

## When to use

- Re-imports after partial failures
- Retries of failed bill creates
- CSV imports with repeated external IDs

## When not to use

- Duplicate invoices from vendors (AP-level dedupe)
- Non-NetSuite ERPs
- UI-entered bills

## Compatibility

NetSuite CSV import, SuiteTalk, REST API.

## Variant phrasings

### duplicate external ID vendor bill

### external ID already exists NetSuite

### idempotent vendor bill import

## Root cause

External IDs are the dedupe key, but integrations often retry a failed create with a new request without checking whether the first attempt actually posted. The second attempt then collides with the first.

## Edge cases

- Partial failures can create the header but not lines; reconcile, do not just skip
- External ID uniqueness is per record type; bills and POs do not collide
- Bulk imports need the check batched, not per row, for performance

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_slQTbIXUm9Y79jXfcs76CQ
