Skip to content

Commit 6917534

Browse files
KyleAMathewsclaude
andauthored
Add some more error types to the error handling doc (#740)
* docs: enhance error-handling guide with transaction and CRUD operation errors Added comprehensive documentation for commonly encountered errors: Transaction Lifecycle Errors: - MissingMutationFunctionError - TransactionNotPendingMutateError - TransactionNotPendingCommitError - TransactionAlreadyCompletedRollbackError Sync Transaction Errors: - NoPendingSyncTransactionWriteError - SyncTransactionAlreadyCommittedWriteError - NoPendingSyncTransactionCommitError - SyncTransactionAlreadyCommittedError CRUD Operation Errors: - UndefinedKeyError - UpdateKeyNotFoundError - KeyUpdateNotAllowedError - DeleteKeyNotFoundError Storage Errors: - SerializationError All new error types include practical examples showing how to catch and handle them, plus guidance on recovery patterns. * docs: remove SerializationError (specific to LocalStorage collection type) --------- Co-authored-by: Claude <[email protected]>
1 parent 01af56d commit 6917534

File tree

1 file changed

+207
-23
lines changed

1 file changed

+207
-23
lines changed

docs/guides/error-handling.md

Lines changed: 207 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,11 @@ todoCollection.insert(newTodo)
258258
// Error: Collection.insert called directly (not within an explicit transaction) but no 'onInsert' handler is configured
259259
```
260260

261-
### Duplicate Key Errors
261+
### Insert Operation Errors
262262

263-
Inserting items with existing keys will throw:
263+
#### DuplicateKeyError
264+
265+
Thrown when inserting items with existing keys:
264266

265267
```ts
266268
import { DuplicateKeyError } from "@tanstack/db"
@@ -270,28 +272,90 @@ try {
270272
} catch (error) {
271273
if (error instanceof DuplicateKeyError) {
272274
console.log(`Duplicate key: ${error.message}`)
275+
// Consider using update() instead, or check if item exists first
273276
}
274277
}
275278
```
276279

277-
### Schema Validation Errors
280+
#### UndefinedKeyError
278281

279-
Schema validation must be synchronous:
282+
Thrown when an object is created without a defined key:
280283

281284
```ts
282-
const todoCollection = createCollection({
285+
import { UndefinedKeyError } from "@tanstack/db"
286+
287+
const collection = createCollection({
283288
id: "todos",
284-
getKey: (todo) => todo.id,
285-
schema: {
286-
"~standard": {
287-
validate: async (data) => { // Async validation not allowed
288-
// ...
289-
}
290-
}
291-
}
289+
getKey: (item) => item.id,
292290
})
293291

294-
// Will throw: Schema validation must be synchronous
292+
try {
293+
collection.insert({ text: "Todo" }) // Missing 'id' field
294+
} catch (error) {
295+
if (error instanceof UndefinedKeyError) {
296+
console.log("Item is missing required key field")
297+
// Ensure your items have the key field defined by getKey
298+
}
299+
}
300+
```
301+
302+
### Update Operation Errors
303+
304+
#### UpdateKeyNotFoundError
305+
306+
Thrown when trying to update a key that doesn't exist in the collection:
307+
308+
```ts
309+
import { UpdateKeyNotFoundError } from "@tanstack/db"
310+
311+
try {
312+
todoCollection.update("nonexistent-key", draft => {
313+
draft.completed = true
314+
})
315+
} catch (error) {
316+
if (error instanceof UpdateKeyNotFoundError) {
317+
console.log("Key not found - item may have been deleted")
318+
// Consider using insert() if the item doesn't exist
319+
}
320+
}
321+
```
322+
323+
#### KeyUpdateNotAllowedError
324+
325+
Thrown when attempting to change an item's key (not allowed - delete and re-insert instead):
326+
327+
```ts
328+
import { KeyUpdateNotAllowedError } from "@tanstack/db"
329+
330+
try {
331+
todoCollection.update("todo-1", draft => {
332+
draft.id = "todo-2" // Not allowed!
333+
})
334+
} catch (error) {
335+
if (error instanceof KeyUpdateNotAllowedError) {
336+
console.log("Cannot change item keys")
337+
// Instead, delete the old item and insert a new one
338+
}
339+
}
340+
```
341+
342+
### Delete Operation Errors
343+
344+
#### DeleteKeyNotFoundError
345+
346+
Thrown when trying to delete a key that doesn't exist:
347+
348+
```ts
349+
import { DeleteKeyNotFoundError } from "@tanstack/db"
350+
351+
try {
352+
todoCollection.delete("nonexistent-key")
353+
} catch (error) {
354+
if (error instanceof DeleteKeyNotFoundError) {
355+
console.log("Key not found - item may have already been deleted")
356+
// This may be acceptable in some scenarios (idempotent deletes)
357+
}
358+
}
295359
```
296360

297361
## Sync Error Handling
@@ -432,22 +496,139 @@ tx2.mutate(() => collection.update("1", draft => { draft.value = "B" })) // Same
432496
tx1.rollback() // tx2 is automatically rolled back
433497
```
434498

435-
### Handling Invalid State Errors
499+
### Transaction Lifecycle Errors
500+
501+
Transactions validate their state before operations to prevent misuse. Here are the specific errors you may encounter:
502+
503+
#### MissingMutationFunctionError
504+
505+
Thrown when creating a transaction without a required `mutationFn`:
506+
507+
```ts
508+
import { MissingMutationFunctionError } from "@tanstack/db"
509+
510+
try {
511+
const tx = createTransaction({}) // Missing mutationFn
512+
} catch (error) {
513+
if (error instanceof MissingMutationFunctionError) {
514+
console.log("mutationFn is required when creating a transaction")
515+
}
516+
}
517+
```
518+
519+
#### TransactionNotPendingMutateError
520+
521+
Thrown when calling `mutate()` after a transaction is no longer pending:
522+
523+
```ts
524+
import { TransactionNotPendingMutateError } from "@tanstack/db"
525+
526+
const tx = createTransaction({ mutationFn: async () => {} })
527+
528+
await tx.commit()
529+
530+
try {
531+
tx.mutate(() => {
532+
collection.insert({ id: "1", text: "Item" })
533+
})
534+
} catch (error) {
535+
if (error instanceof TransactionNotPendingMutateError) {
536+
console.log("Cannot mutate - transaction is no longer pending")
537+
}
538+
}
539+
```
540+
541+
#### TransactionNotPendingCommitError
542+
543+
Thrown when calling `commit()` after a transaction is no longer pending:
544+
545+
```ts
546+
import { TransactionNotPendingCommitError } from "@tanstack/db"
547+
548+
const tx = createTransaction({ mutationFn: async () => {} })
549+
tx.mutate(() => collection.insert({ id: "1", text: "Item" }))
550+
551+
await tx.commit()
552+
553+
try {
554+
await tx.commit() // Trying to commit again
555+
} catch (error) {
556+
if (error instanceof TransactionNotPendingCommitError) {
557+
console.log("Transaction already committed")
558+
}
559+
}
560+
```
561+
562+
#### TransactionAlreadyCompletedRollbackError
436563

437-
Transactions validate their state before operations:
564+
Thrown when calling `rollback()` on a transaction that's already completed:
438565

439566
```ts
567+
import { TransactionAlreadyCompletedRollbackError } from "@tanstack/db"
568+
440569
const tx = createTransaction({ mutationFn: async () => {} })
570+
tx.mutate(() => collection.insert({ id: "1", text: "Item" }))
441571

442-
// Complete the transaction
443572
await tx.commit()
444573

445-
// These will throw:
446-
tx.mutate(() => {}) // Error: You can no longer call .mutate() as the transaction is no longer pending
447-
tx.commit() // Error: You can no longer call .commit() as the transaction is no longer pending
448-
tx.rollback() // Error: You can no longer call .rollback() as the transaction is already completed
574+
try {
575+
tx.rollback() // Can't rollback after commit
576+
} catch (error) {
577+
if (error instanceof TransactionAlreadyCompletedRollbackError) {
578+
console.log("Cannot rollback - transaction already completed")
579+
}
580+
}
449581
```
450582

583+
### Sync Transaction Errors
584+
585+
When working with sync transactions, these errors can occur:
586+
587+
#### NoPendingSyncTransactionWriteError
588+
589+
Thrown when calling `write()` without an active sync transaction:
590+
591+
```ts
592+
const collection = createCollection({
593+
id: "todos",
594+
sync: {
595+
sync: ({ write }) => {
596+
// Calling write without begin() first
597+
write({ type: "insert", value: { id: "1", text: "Todo" } })
598+
// Error: No pending sync transaction to write to
599+
}
600+
}
601+
})
602+
```
603+
604+
#### SyncTransactionAlreadyCommittedWriteError
605+
606+
Thrown when calling `write()` after the sync transaction is already committed:
607+
608+
```ts
609+
const collection = createCollection({
610+
id: "todos",
611+
sync: {
612+
sync: ({ begin, write, commit }) => {
613+
begin()
614+
commit()
615+
616+
// Trying to write after commit
617+
write({ type: "insert", value: { id: "1", text: "Todo" } })
618+
// Error: The pending sync transaction is already committed
619+
}
620+
}
621+
})
622+
```
623+
624+
#### NoPendingSyncTransactionCommitError
625+
626+
Thrown when calling `commit()` without an active sync transaction.
627+
628+
#### SyncTransactionAlreadyCommittedError
629+
630+
Thrown when calling `commit()` on a sync transaction that's already committed.
631+
451632
## Best Practices
452633

453634
1. **Use instanceof checks** - Use `instanceof` instead of string matching for error handling:
@@ -471,10 +652,13 @@ tx.rollback() // Error: You can no longer call .rollback() as the transaction is
471652
## Example: Complete Error Handling
472653

473654
```tsx
474-
import {
475-
createCollection,
655+
import {
656+
createCollection,
476657
SchemaValidationError,
477658
DuplicateKeyError,
659+
UpdateKeyNotFoundError,
660+
DeleteKeyNotFoundError,
661+
TransactionNotPendingCommitError,
478662
createTransaction
479663
} from "@tanstack/db"
480664
import { useLiveQuery } from "@tanstack/react-db"

0 commit comments

Comments
 (0)