@mastra/lance 0.1.3-alpha.0 → 0.1.3-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@ import { connect } from '@lancedb/lancedb';
2
2
  import type { Connection, ConnectionOptions, SchemaLike, FieldLike } from '@lancedb/lancedb';
3
3
  import type { MastraMessageContentV2 } from '@mastra/core/agent';
4
4
  import { MessageList } from '@mastra/core/agent';
5
+ import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
5
6
  import type { MastraMessageV1, MastraMessageV2, StorageThreadType, TraceType } from '@mastra/core/memory';
6
7
  import {
7
8
  MastraStorage,
@@ -57,10 +58,30 @@ export class LanceStorage extends MastraStorage {
57
58
  instance.lanceClient = await connect(uri, options);
58
59
  return instance;
59
60
  } catch (e: any) {
60
- throw new Error(`Failed to connect to LanceDB: ${e}`);
61
+ throw new MastraError(
62
+ {
63
+ id: 'STORAGE_LANCE_STORAGE_CONNECT_FAILED',
64
+ domain: ErrorDomain.STORAGE,
65
+ category: ErrorCategory.THIRD_PARTY,
66
+ text: `Failed to connect to LanceDB: ${e.message || e}`,
67
+ details: { uri, optionsProvided: !!options },
68
+ },
69
+ e,
70
+ );
61
71
  }
62
72
  }
63
73
 
74
+ private getPrimaryKeys(tableName: TABLE_NAMES): string[] {
75
+ let primaryId: string[] = ['id'];
76
+ if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
77
+ primaryId = ['workflow_name', 'run_id'];
78
+ } else if (tableName === TABLE_EVALS) {
79
+ primaryId = ['agent_name', 'metric_name', 'run_id'];
80
+ }
81
+
82
+ return primaryId;
83
+ }
84
+
64
85
  /**
65
86
  * @internal
66
87
  * Private constructor to enforce using the create factory method
@@ -76,11 +97,41 @@ export class LanceStorage extends MastraStorage {
76
97
  tableName: TABLE_NAMES;
77
98
  schema: Record<string, StorageColumn>;
78
99
  }): Promise<void> {
100
+ try {
101
+ if (!this.lanceClient) {
102
+ throw new Error('LanceDB client not initialized. Call LanceStorage.create() first.');
103
+ }
104
+ if (!tableName) {
105
+ throw new Error('tableName is required for createTable.');
106
+ }
107
+ if (!schema) {
108
+ throw new Error('schema is required for createTable.');
109
+ }
110
+ } catch (error) {
111
+ throw new MastraError(
112
+ {
113
+ id: 'STORAGE_LANCE_STORAGE_CREATE_TABLE_INVALID_ARGS',
114
+ domain: ErrorDomain.STORAGE,
115
+ category: ErrorCategory.USER,
116
+ details: { tableName },
117
+ },
118
+ error,
119
+ );
120
+ }
121
+
79
122
  try {
80
123
  const arrowSchema = this.translateSchema(schema);
81
124
  await this.lanceClient.createEmptyTable(tableName, arrowSchema);
82
125
  } catch (error: any) {
83
- throw new Error(`Failed to create table: ${error}`);
126
+ throw new MastraError(
127
+ {
128
+ id: 'STORAGE_LANCE_STORAGE_CREATE_TABLE_FAILED',
129
+ domain: ErrorDomain.STORAGE,
130
+ category: ErrorCategory.THIRD_PARTY,
131
+ details: { tableName },
132
+ },
133
+ error,
134
+ );
84
135
  }
85
136
  }
86
137
 
@@ -130,15 +181,42 @@ export class LanceStorage extends MastraStorage {
130
181
  * @param tableName Name of the table to drop
131
182
  */
132
183
  async dropTable(tableName: TABLE_NAMES): Promise<void> {
184
+ try {
185
+ if (!this.lanceClient) {
186
+ throw new Error('LanceDB client not initialized. Call LanceStorage.create() first.');
187
+ }
188
+ if (!tableName) {
189
+ throw new Error('tableName is required for dropTable.');
190
+ }
191
+ } catch (validationError: any) {
192
+ throw new MastraError(
193
+ {
194
+ id: 'STORAGE_LANCE_STORAGE_DROP_TABLE_INVALID_ARGS',
195
+ domain: ErrorDomain.STORAGE,
196
+ category: ErrorCategory.USER,
197
+ text: validationError.message,
198
+ details: { tableName },
199
+ },
200
+ validationError,
201
+ );
202
+ }
203
+
133
204
  try {
134
205
  await this.lanceClient.dropTable(tableName);
135
206
  } catch (error: any) {
136
- // Don't throw if the table doesn't exist
137
- if (error.toString().includes('was not found')) {
207
+ if (error.toString().includes('was not found') || error.message?.includes('Table not found')) {
138
208
  this.logger.debug(`Table '${tableName}' does not exist, skipping drop`);
139
209
  return;
140
210
  }
141
- throw new Error(`Failed to drop table: ${error}`);
211
+ throw new MastraError(
212
+ {
213
+ id: 'STORAGE_LANCE_STORAGE_DROP_TABLE_FAILED',
214
+ domain: ErrorDomain.STORAGE,
215
+ category: ErrorCategory.THIRD_PARTY,
216
+ details: { tableName },
217
+ },
218
+ error,
219
+ );
142
220
  }
143
221
  }
144
222
 
@@ -148,6 +226,26 @@ export class LanceStorage extends MastraStorage {
148
226
  * @returns Table schema
149
227
  */
150
228
  async getTableSchema(tableName: TABLE_NAMES): Promise<SchemaLike> {
229
+ try {
230
+ if (!this.lanceClient) {
231
+ throw new Error('LanceDB client not initialized. Call LanceStorage.create() first.');
232
+ }
233
+ if (!tableName) {
234
+ throw new Error('tableName is required for getTableSchema.');
235
+ }
236
+ } catch (validationError: any) {
237
+ throw new MastraError(
238
+ {
239
+ id: 'STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_INVALID_ARGS',
240
+ domain: ErrorDomain.STORAGE,
241
+ category: ErrorCategory.USER,
242
+ text: validationError.message,
243
+ details: { tableName },
244
+ },
245
+ validationError,
246
+ );
247
+ }
248
+
151
249
  try {
152
250
  const table = await this.lanceClient.openTable(tableName);
153
251
  const rawSchema = await table.schema();
@@ -162,7 +260,15 @@ export class LanceStorage extends MastraStorage {
162
260
  },
163
261
  };
164
262
  } catch (error: any) {
165
- throw new Error(`Failed to get table schema: ${error}`);
263
+ throw new MastraError(
264
+ {
265
+ id: 'STORAGE_LANCE_STORAGE_GET_TABLE_SCHEMA_FAILED',
266
+ domain: ErrorDomain.STORAGE,
267
+ category: ErrorCategory.THIRD_PARTY,
268
+ details: { tableName },
269
+ },
270
+ error,
271
+ );
166
272
  }
167
273
  }
168
274
 
@@ -199,43 +305,114 @@ export class LanceStorage extends MastraStorage {
199
305
  schema: Record<string, StorageColumn>;
200
306
  ifNotExists: string[];
201
307
  }): Promise<void> {
202
- const table = await this.lanceClient.openTable(tableName);
203
- const currentSchema = await table.schema();
204
- const existingFields = new Set(currentSchema.fields.map((f: any) => f.name));
205
-
206
- const typeMap: Record<string, string> = {
207
- text: 'string',
208
- integer: 'int',
209
- bigint: 'bigint',
210
- timestamp: 'timestamp',
211
- jsonb: 'string',
212
- uuid: 'string',
213
- };
308
+ try {
309
+ if (!this.lanceClient) {
310
+ throw new Error('LanceDB client not initialized. Call LanceStorage.create() first.');
311
+ }
312
+ if (!tableName) {
313
+ throw new Error('tableName is required for alterTable.');
314
+ }
315
+ if (!schema) {
316
+ throw new Error('schema is required for alterTable.');
317
+ }
318
+ if (!ifNotExists || ifNotExists.length === 0) {
319
+ this.logger.debug('No columns specified to add in alterTable, skipping.');
320
+ return;
321
+ }
322
+ } catch (validationError: any) {
323
+ throw new MastraError(
324
+ {
325
+ id: 'STORAGE_LANCE_STORAGE_ALTER_TABLE_INVALID_ARGS',
326
+ domain: ErrorDomain.STORAGE,
327
+ category: ErrorCategory.USER,
328
+ text: validationError.message,
329
+ details: { tableName },
330
+ },
331
+ validationError,
332
+ );
333
+ }
214
334
 
215
- // Find columns to add
216
- const columnsToAdd = ifNotExists
217
- .filter(col => schema[col] && !existingFields.has(col))
218
- .map(col => {
219
- const colDef = schema[col];
220
- return {
221
- name: col,
222
- valueSql: colDef?.nullable
223
- ? `cast(NULL as ${typeMap[colDef.type ?? 'text']})`
224
- : `cast(${this.getDefaultValue(colDef?.type ?? 'text')} as ${typeMap[colDef?.type ?? 'text']})`,
225
- };
226
- });
335
+ try {
336
+ const table = await this.lanceClient.openTable(tableName);
337
+ const currentSchema = await table.schema();
338
+ const existingFields = new Set(currentSchema.fields.map((f: any) => f.name));
339
+
340
+ const typeMap: Record<string, string> = {
341
+ text: 'string',
342
+ integer: 'int',
343
+ bigint: 'bigint',
344
+ timestamp: 'timestamp',
345
+ jsonb: 'string',
346
+ uuid: 'string',
347
+ };
227
348
 
228
- if (columnsToAdd.length > 0) {
229
- await table.addColumns(columnsToAdd);
230
- this.logger?.info?.(`Added columns [${columnsToAdd.map(c => c.name).join(', ')}] to table ${tableName}`);
349
+ // Find columns to add
350
+ const columnsToAdd = ifNotExists
351
+ .filter(col => schema[col] && !existingFields.has(col))
352
+ .map(col => {
353
+ const colDef = schema[col];
354
+ return {
355
+ name: col,
356
+ valueSql: colDef?.nullable
357
+ ? `cast(NULL as ${typeMap[colDef.type ?? 'text']})`
358
+ : `cast(${this.getDefaultValue(colDef?.type ?? 'text')} as ${typeMap[colDef?.type ?? 'text']})`,
359
+ };
360
+ });
361
+
362
+ if (columnsToAdd.length > 0) {
363
+ await table.addColumns(columnsToAdd);
364
+ this.logger?.info?.(`Added columns [${columnsToAdd.map(c => c.name).join(', ')}] to table ${tableName}`);
365
+ }
366
+ } catch (error: any) {
367
+ throw new MastraError(
368
+ {
369
+ id: 'STORAGE_LANCE_STORAGE_ALTER_TABLE_FAILED',
370
+ domain: ErrorDomain.STORAGE,
371
+ category: ErrorCategory.THIRD_PARTY,
372
+ details: { tableName },
373
+ },
374
+ error,
375
+ );
231
376
  }
232
377
  }
233
378
 
234
379
  async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
235
- const table = await this.lanceClient.openTable(tableName);
380
+ try {
381
+ if (!this.lanceClient) {
382
+ throw new Error('LanceDB client not initialized. Call LanceStorage.create() first.');
383
+ }
384
+ if (!tableName) {
385
+ throw new Error('tableName is required for clearTable.');
386
+ }
387
+ } catch (validationError: any) {
388
+ throw new MastraError(
389
+ {
390
+ id: 'STORAGE_LANCE_STORAGE_CLEAR_TABLE_INVALID_ARGS',
391
+ domain: ErrorDomain.STORAGE,
392
+ category: ErrorCategory.USER,
393
+ text: validationError.message,
394
+ details: { tableName },
395
+ },
396
+ validationError,
397
+ );
398
+ }
399
+
400
+ try {
401
+ const table = await this.lanceClient.openTable(tableName);
236
402
 
237
- // delete function always takes a predicate as an argument, so we use '1=1' to delete all records because it is always true.
238
- await table.delete('1=1');
403
+ // delete function always takes a predicate as an argument, so we use '1=1' to delete all records because it is always true.
404
+ await table.delete('1=1');
405
+ } catch (error: any) {
406
+ throw new MastraError(
407
+ {
408
+ id: 'STORAGE_LANCE_STORAGE_CLEAR_TABLE_FAILED',
409
+ domain: ErrorDomain.STORAGE,
410
+ category: ErrorCategory.THIRD_PARTY,
411
+ details: { tableName },
412
+ },
413
+ error,
414
+ );
415
+ }
239
416
  }
240
417
 
241
418
  /**
@@ -244,9 +421,34 @@ export class LanceStorage extends MastraStorage {
244
421
  * @param record The record to insert.
245
422
  */
246
423
  async insert({ tableName, record }: { tableName: string; record: Record<string, any> }): Promise<void> {
424
+ try {
425
+ if (!this.lanceClient) {
426
+ throw new Error('LanceDB client not initialized. Call LanceStorage.create() first.');
427
+ }
428
+ if (!tableName) {
429
+ throw new Error('tableName is required for insert.');
430
+ }
431
+ if (!record || Object.keys(record).length === 0) {
432
+ throw new Error('record is required and cannot be empty for insert.');
433
+ }
434
+ } catch (validationError: any) {
435
+ throw new MastraError(
436
+ {
437
+ id: 'STORAGE_LANCE_STORAGE_INSERT_INVALID_ARGS',
438
+ domain: ErrorDomain.STORAGE,
439
+ category: ErrorCategory.USER,
440
+ text: validationError.message,
441
+ details: { tableName },
442
+ },
443
+ validationError,
444
+ );
445
+ }
446
+
247
447
  try {
248
448
  const table = await this.lanceClient.openTable(tableName);
249
449
 
450
+ const primaryId = this.getPrimaryKeys(tableName as TABLE_NAMES);
451
+
250
452
  const processedRecord = { ...record };
251
453
 
252
454
  for (const key in processedRecord) {
@@ -260,9 +462,17 @@ export class LanceStorage extends MastraStorage {
260
462
  }
261
463
  }
262
464
 
263
- await table.add([processedRecord], { mode: 'overwrite' });
465
+ await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([processedRecord]);
264
466
  } catch (error: any) {
265
- throw new Error(`Failed to insert record: ${error}`);
467
+ throw new MastraError(
468
+ {
469
+ id: 'STORAGE_LANCE_STORAGE_INSERT_FAILED',
470
+ domain: ErrorDomain.STORAGE,
471
+ category: ErrorCategory.THIRD_PARTY,
472
+ details: { tableName },
473
+ },
474
+ error,
475
+ );
266
476
  }
267
477
  }
268
478
 
@@ -272,9 +482,34 @@ export class LanceStorage extends MastraStorage {
272
482
  * @param records The records to insert.
273
483
  */
274
484
  async batchInsert({ tableName, records }: { tableName: string; records: Record<string, any>[] }): Promise<void> {
485
+ try {
486
+ if (!this.lanceClient) {
487
+ throw new Error('LanceDB client not initialized. Call LanceStorage.create() first.');
488
+ }
489
+ if (!tableName) {
490
+ throw new Error('tableName is required for batchInsert.');
491
+ }
492
+ if (!records || records.length === 0) {
493
+ throw new Error('records array is required and cannot be empty for batchInsert.');
494
+ }
495
+ } catch (validationError: any) {
496
+ throw new MastraError(
497
+ {
498
+ id: 'STORAGE_LANCE_STORAGE_BATCH_INSERT_INVALID_ARGS',
499
+ domain: ErrorDomain.STORAGE,
500
+ category: ErrorCategory.USER,
501
+ text: validationError.message,
502
+ details: { tableName },
503
+ },
504
+ validationError,
505
+ );
506
+ }
507
+
275
508
  try {
276
509
  const table = await this.lanceClient.openTable(tableName);
277
510
 
511
+ const primaryId = this.getPrimaryKeys(tableName as TABLE_NAMES);
512
+
278
513
  const processedRecords = records.map(record => {
279
514
  const processedRecord = { ...record };
280
515
 
@@ -295,9 +530,17 @@ export class LanceStorage extends MastraStorage {
295
530
  return processedRecord;
296
531
  });
297
532
 
298
- await table.add(processedRecords, { mode: 'overwrite' });
533
+ await table.mergeInsert(primaryId).whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(processedRecords);
299
534
  } catch (error: any) {
300
- throw new Error(`Failed to batch insert records: ${error}`);
535
+ throw new MastraError(
536
+ {
537
+ id: 'STORAGE_LANCE_STORAGE_BATCH_INSERT_FAILED',
538
+ domain: ErrorDomain.STORAGE,
539
+ category: ErrorCategory.THIRD_PARTY,
540
+ details: { tableName },
541
+ },
542
+ error,
543
+ );
301
544
  }
302
545
  }
303
546
 
@@ -309,6 +552,29 @@ export class LanceStorage extends MastraStorage {
309
552
  * @returns The loaded record with proper type conversions, or null if not found
310
553
  */
311
554
  async load({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, any> }): Promise<any> {
555
+ try {
556
+ if (!this.lanceClient) {
557
+ throw new Error('LanceDB client not initialized. Call LanceStorage.create() first.');
558
+ }
559
+ if (!tableName) {
560
+ throw new Error('tableName is required for load.');
561
+ }
562
+ if (!keys || Object.keys(keys).length === 0) {
563
+ throw new Error('keys are required and cannot be empty for load.');
564
+ }
565
+ } catch (validationError: any) {
566
+ throw new MastraError(
567
+ {
568
+ id: 'STORAGE_LANCE_STORAGE_LOAD_INVALID_ARGS',
569
+ domain: ErrorDomain.STORAGE,
570
+ category: ErrorCategory.USER,
571
+ text: validationError.message,
572
+ details: { tableName },
573
+ },
574
+ validationError,
575
+ );
576
+ }
577
+
312
578
  try {
313
579
  const table = await this.lanceClient.openTable(tableName);
314
580
  const tableSchema = await this.getTableSchema(tableName);
@@ -351,7 +617,17 @@ export class LanceStorage extends MastraStorage {
351
617
  // Process the result with type conversions
352
618
  return this.processResultWithTypeConversion(result[0], tableSchema);
353
619
  } catch (error: any) {
354
- throw new Error(`Failed to load record: ${error}`);
620
+ // If it's already a MastraError (e.g. from validateKeyTypes if we change it later), rethrow
621
+ if (error instanceof MastraError) throw error;
622
+ throw new MastraError(
623
+ {
624
+ id: 'STORAGE_LANCE_STORAGE_LOAD_FAILED',
625
+ domain: ErrorDomain.STORAGE,
626
+ category: ErrorCategory.THIRD_PARTY,
627
+ details: { tableName, keyCount: Object.keys(keys).length, firstKey: Object.keys(keys)[0] ?? '' },
628
+ },
629
+ error,
630
+ );
355
631
  }
356
632
  }
357
633
 
@@ -457,7 +733,14 @@ export class LanceStorage extends MastraStorage {
457
733
  try {
458
734
  return this.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
459
735
  } catch (error: any) {
460
- throw new Error(`Failed to get thread by ID: ${error}`);
736
+ throw new MastraError(
737
+ {
738
+ id: 'LANCE_STORE_GET_THREAD_BY_ID_FAILED',
739
+ domain: ErrorDomain.STORAGE,
740
+ category: ErrorCategory.THIRD_PARTY,
741
+ },
742
+ error,
743
+ );
461
744
  }
462
745
  }
463
746
 
@@ -473,7 +756,14 @@ export class LanceStorage extends MastraStorage {
473
756
  await this.getTableSchema(TABLE_THREADS),
474
757
  ) as StorageThreadType[];
475
758
  } catch (error: any) {
476
- throw new Error(`Failed to get threads by resource ID: ${error}`);
759
+ throw new MastraError(
760
+ {
761
+ id: 'LANCE_STORE_GET_THREADS_BY_RESOURCE_ID_FAILED',
762
+ domain: ErrorDomain.STORAGE,
763
+ category: ErrorCategory.THIRD_PARTY,
764
+ },
765
+ error,
766
+ );
477
767
  }
478
768
  }
479
769
 
@@ -490,7 +780,14 @@ export class LanceStorage extends MastraStorage {
490
780
 
491
781
  return thread;
492
782
  } catch (error: any) {
493
- throw new Error(`Failed to save thread: ${error}`);
783
+ throw new MastraError(
784
+ {
785
+ id: 'LANCE_STORE_SAVE_THREAD_FAILED',
786
+ domain: ErrorDomain.STORAGE,
787
+ category: ErrorCategory.THIRD_PARTY,
788
+ },
789
+ error,
790
+ );
494
791
  }
495
792
  }
496
793
 
@@ -506,7 +803,7 @@ export class LanceStorage extends MastraStorage {
506
803
  try {
507
804
  const record = { id, title, metadata: JSON.stringify(metadata) };
508
805
  const table = await this.lanceClient.openTable(TABLE_THREADS);
509
- await table.add([record], { mode: 'overwrite' });
806
+ await table.mergeInsert('id').whenMatchedUpdateAll().whenNotMatchedInsertAll().execute([record]);
510
807
 
511
808
  const query = table.query().where(`id = '${id}'`);
512
809
 
@@ -516,7 +813,14 @@ export class LanceStorage extends MastraStorage {
516
813
  await this.getTableSchema(TABLE_THREADS),
517
814
  ) as StorageThreadType;
518
815
  } catch (error: any) {
519
- throw new Error(`Failed to update thread: ${error}`);
816
+ throw new MastraError(
817
+ {
818
+ id: 'LANCE_STORE_UPDATE_THREAD_FAILED',
819
+ domain: ErrorDomain.STORAGE,
820
+ category: ErrorCategory.THIRD_PARTY,
821
+ },
822
+ error,
823
+ );
520
824
  }
521
825
  }
522
826
 
@@ -525,7 +829,14 @@ export class LanceStorage extends MastraStorage {
525
829
  const table = await this.lanceClient.openTable(TABLE_THREADS);
526
830
  await table.delete(`id = '${threadId}'`);
527
831
  } catch (error: any) {
528
- throw new Error(`Failed to delete thread: ${error}`);
832
+ throw new MastraError(
833
+ {
834
+ id: 'LANCE_STORE_DELETE_THREAD_FAILED',
835
+ domain: ErrorDomain.STORAGE,
836
+ category: ErrorCategory.THIRD_PARTY,
837
+ },
838
+ error,
839
+ );
529
840
  }
530
841
  }
531
842
 
@@ -618,7 +929,7 @@ export class LanceStorage extends MastraStorage {
618
929
  if (threadConfig) {
619
930
  throw new Error('ThreadConfig is not supported by LanceDB storage');
620
931
  }
621
-
932
+ const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: Number.MAX_SAFE_INTEGER });
622
933
  const table = await this.lanceClient.openTable(TABLE_MESSAGES);
623
934
  let query = table.query().where(`\`threadId\` = '${threadId}'`);
624
935
 
@@ -653,8 +964,8 @@ export class LanceStorage extends MastraStorage {
653
964
  }
654
965
 
655
966
  // If we're fetching the last N messages, take only the last N after sorting
656
- if (selectBy?.last !== undefined && selectBy.last !== false) {
657
- records = records.slice(-selectBy.last);
967
+ if (limit !== Number.MAX_SAFE_INTEGER) {
968
+ records = records.slice(-limit);
658
969
  }
659
970
 
660
971
  const messages = this.processResultWithTypeConversion(records, await this.getTableSchema(TABLE_MESSAGES));
@@ -675,7 +986,14 @@ export class LanceStorage extends MastraStorage {
675
986
  if (format === 'v2') return list.get.all.v2();
676
987
  return list.get.all.v1();
677
988
  } catch (error: any) {
678
- throw new Error(`Failed to get messages: ${error}`);
989
+ throw new MastraError(
990
+ {
991
+ id: 'LANCE_STORE_GET_MESSAGES_FAILED',
992
+ domain: ErrorDomain.STORAGE,
993
+ category: ErrorCategory.THIRD_PARTY,
994
+ },
995
+ error,
996
+ );
679
997
  }
680
998
  }
681
999
 
@@ -702,12 +1020,20 @@ export class LanceStorage extends MastraStorage {
702
1020
  }));
703
1021
 
704
1022
  const table = await this.lanceClient.openTable(TABLE_MESSAGES);
705
- await table.add(transformedMessages, { mode: 'overwrite' });
1023
+ await table.mergeInsert('id').whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(transformedMessages);
1024
+
706
1025
  const list = new MessageList().add(messages, 'memory');
707
1026
  if (format === `v2`) return list.get.all.v2();
708
1027
  return list.get.all.v1();
709
1028
  } catch (error: any) {
710
- throw new Error(`Failed to save messages: ${error}`);
1029
+ throw new MastraError(
1030
+ {
1031
+ id: 'LANCE_STORE_SAVE_MESSAGES_FAILED',
1032
+ domain: ErrorDomain.STORAGE,
1033
+ category: ErrorCategory.THIRD_PARTY,
1034
+ },
1035
+ error,
1036
+ );
711
1037
  }
712
1038
  }
713
1039
 
@@ -726,7 +1052,14 @@ export class LanceStorage extends MastraStorage {
726
1052
 
727
1053
  return trace;
728
1054
  } catch (error: any) {
729
- throw new Error(`Failed to save trace: ${error}`);
1055
+ throw new MastraError(
1056
+ {
1057
+ id: 'LANCE_STORE_SAVE_TRACE_FAILED',
1058
+ domain: ErrorDomain.STORAGE,
1059
+ category: ErrorCategory.THIRD_PARTY,
1060
+ },
1061
+ error,
1062
+ );
730
1063
  }
731
1064
  }
732
1065
 
@@ -737,7 +1070,14 @@ export class LanceStorage extends MastraStorage {
737
1070
  const records = await query.toArray();
738
1071
  return this.processResultWithTypeConversion(records[0], await this.getTableSchema(TABLE_TRACES)) as TraceType;
739
1072
  } catch (error: any) {
740
- throw new Error(`Failed to get trace by ID: ${error}`);
1073
+ throw new MastraError(
1074
+ {
1075
+ id: 'LANCE_STORE_GET_TRACE_BY_ID_FAILED',
1076
+ domain: ErrorDomain.STORAGE,
1077
+ category: ErrorCategory.THIRD_PARTY,
1078
+ },
1079
+ error,
1080
+ );
741
1081
  }
742
1082
  }
743
1083
 
@@ -796,7 +1136,15 @@ export class LanceStorage extends MastraStorage {
796
1136
  };
797
1137
  }) as TraceType[];
798
1138
  } catch (error: any) {
799
- throw new Error(`Failed to get traces: ${error}`);
1139
+ throw new MastraError(
1140
+ {
1141
+ id: 'LANCE_STORE_GET_TRACES_FAILED',
1142
+ domain: ErrorDomain.STORAGE,
1143
+ category: ErrorCategory.THIRD_PARTY,
1144
+ details: { name: name ?? '', scope: scope ?? '' },
1145
+ },
1146
+ error,
1147
+ );
800
1148
  }
801
1149
  }
802
1150
 
@@ -819,7 +1167,14 @@ export class LanceStorage extends MastraStorage {
819
1167
  await table.add(transformedEvals, { mode: 'append' });
820
1168
  return evals;
821
1169
  } catch (error: any) {
822
- throw new Error(`Failed to save evals: ${error}`);
1170
+ throw new MastraError(
1171
+ {
1172
+ id: 'LANCE_STORE_SAVE_EVALS_FAILED',
1173
+ domain: ErrorDomain.STORAGE,
1174
+ category: ErrorCategory.THIRD_PARTY,
1175
+ },
1176
+ error,
1177
+ );
823
1178
  }
824
1179
  }
825
1180
 
@@ -847,7 +1202,15 @@ export class LanceStorage extends MastraStorage {
847
1202
  };
848
1203
  }) as EvalRow[];
849
1204
  } catch (error: any) {
850
- throw new Error(`Failed to get evals by agent name: ${error}`);
1205
+ throw new MastraError(
1206
+ {
1207
+ id: 'LANCE_STORE_GET_EVALS_BY_AGENT_NAME_FAILED',
1208
+ domain: ErrorDomain.STORAGE,
1209
+ category: ErrorCategory.THIRD_PARTY,
1210
+ details: { agentName },
1211
+ },
1212
+ error,
1213
+ );
851
1214
  }
852
1215
  }
853
1216
 
@@ -910,7 +1273,15 @@ export class LanceStorage extends MastraStorage {
910
1273
  total: records.length,
911
1274
  };
912
1275
  } catch (error: any) {
913
- throw new Error(`Failed to get workflow runs: ${error}`);
1276
+ throw new MastraError(
1277
+ {
1278
+ id: 'LANCE_STORE_GET_WORKFLOW_RUNS_FAILED',
1279
+ domain: ErrorDomain.STORAGE,
1280
+ category: ErrorCategory.THIRD_PARTY,
1281
+ details: { namespace: args?.namespace ?? '', workflowName: args?.workflowName ?? '' },
1282
+ },
1283
+ error,
1284
+ );
914
1285
  }
915
1286
  }
916
1287
 
@@ -938,7 +1309,15 @@ export class LanceStorage extends MastraStorage {
938
1309
  const record = records[0];
939
1310
  return this.parseWorkflowRun(record);
940
1311
  } catch (error: any) {
941
- throw new Error(`Failed to get workflow run by id: ${error}`);
1312
+ throw new MastraError(
1313
+ {
1314
+ id: 'LANCE_STORE_GET_WORKFLOW_RUN_BY_ID_FAILED',
1315
+ domain: ErrorDomain.STORAGE,
1316
+ category: ErrorCategory.THIRD_PARTY,
1317
+ details: { runId: args.runId, workflowName: args.workflowName ?? '' },
1318
+ },
1319
+ error,
1320
+ );
942
1321
  }
943
1322
  }
944
1323
 
@@ -959,11 +1338,9 @@ export class LanceStorage extends MastraStorage {
959
1338
  const records = await query.toArray();
960
1339
  let createdAt: number;
961
1340
  const now = Date.now();
962
- let mode: 'append' | 'overwrite' = 'append';
963
1341
 
964
1342
  if (records.length > 0) {
965
1343
  createdAt = records[0].createdAt ?? now;
966
- mode = 'overwrite';
967
1344
  } else {
968
1345
  createdAt = now;
969
1346
  }
@@ -976,9 +1353,21 @@ export class LanceStorage extends MastraStorage {
976
1353
  updatedAt: now,
977
1354
  };
978
1355
 
979
- await table.add([record], { mode });
1356
+ await table
1357
+ .mergeInsert(['workflow_name', 'run_id'])
1358
+ .whenMatchedUpdateAll()
1359
+ .whenNotMatchedInsertAll()
1360
+ .execute([record]);
980
1361
  } catch (error: any) {
981
- throw new Error(`Failed to persist workflow snapshot: ${error}`);
1362
+ throw new MastraError(
1363
+ {
1364
+ id: 'LANCE_STORE_PERSIST_WORKFLOW_SNAPSHOT_FAILED',
1365
+ domain: ErrorDomain.STORAGE,
1366
+ category: ErrorCategory.THIRD_PARTY,
1367
+ details: { workflowName, runId },
1368
+ },
1369
+ error,
1370
+ );
982
1371
  }
983
1372
  }
984
1373
  async loadWorkflowSnapshot({
@@ -994,12 +1383,27 @@ export class LanceStorage extends MastraStorage {
994
1383
  const records = await query.toArray();
995
1384
  return records.length > 0 ? JSON.parse(records[0].snapshot) : null;
996
1385
  } catch (error: any) {
997
- throw new Error(`Failed to load workflow snapshot: ${error}`);
1386
+ throw new MastraError(
1387
+ {
1388
+ id: 'LANCE_STORE_LOAD_WORKFLOW_SNAPSHOT_FAILED',
1389
+ domain: ErrorDomain.STORAGE,
1390
+ category: ErrorCategory.THIRD_PARTY,
1391
+ details: { workflowName, runId },
1392
+ },
1393
+ error,
1394
+ );
998
1395
  }
999
1396
  }
1000
1397
 
1001
1398
  async getTracesPaginated(_args: StorageGetTracesArg): Promise<PaginationInfo & { traces: Trace[] }> {
1002
- throw new Error('Method not implemented.');
1399
+ throw new MastraError(
1400
+ {
1401
+ id: 'LANCE_STORE_GET_TRACES_PAGINATED_FAILED',
1402
+ domain: ErrorDomain.STORAGE,
1403
+ category: ErrorCategory.THIRD_PARTY,
1404
+ },
1405
+ 'Method not implemented.',
1406
+ );
1003
1407
  }
1004
1408
 
1005
1409
  async getThreadsByResourceIdPaginated(_args: {
@@ -1007,13 +1411,27 @@ export class LanceStorage extends MastraStorage {
1007
1411
  page?: number;
1008
1412
  perPage?: number;
1009
1413
  }): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
1010
- throw new Error('Method not implemented.');
1414
+ throw new MastraError(
1415
+ {
1416
+ id: 'LANCE_STORE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_FAILED',
1417
+ domain: ErrorDomain.STORAGE,
1418
+ category: ErrorCategory.THIRD_PARTY,
1419
+ },
1420
+ 'Method not implemented.',
1421
+ );
1011
1422
  }
1012
1423
 
1013
1424
  async getMessagesPaginated(
1014
1425
  _args: StorageGetMessagesArg,
1015
1426
  ): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
1016
- throw new Error('Method not implemented.');
1427
+ throw new MastraError(
1428
+ {
1429
+ id: 'LANCE_STORE_GET_MESSAGES_PAGINATED_FAILED',
1430
+ domain: ErrorDomain.STORAGE,
1431
+ category: ErrorCategory.THIRD_PARTY,
1432
+ },
1433
+ 'Method not implemented.',
1434
+ );
1017
1435
  }
1018
1436
 
1019
1437
  async updateMessages(_args: {