Skip to content

Commit 543e8a9

Browse files
committed
Improved error descriptions when the error itself is not logged.
1 parent 1179bc7 commit 543e8a9

File tree

1 file changed

+84
-17
lines changed

1 file changed

+84
-17
lines changed

packages/powersync/lib/src/streaming_sync.dart

Lines changed: 84 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ class StreamingSyncImplementation {
6363
await streamingSyncIteration();
6464
// Continue immediately
6565
} catch (e, stacktrace) {
66-
log.warning('Sync error', e, stacktrace);
66+
final message = _syncErrorMessage(e);
67+
log.warning('Sync error: $message', e, stacktrace);
6768
invalidCredentials = true;
6869

6970
_updateStatus(
@@ -124,7 +125,7 @@ class StreamingSyncImplementation {
124125
Future<String> getWriteCheckpoint() async {
125126
final credentials = await credentialsCallback();
126127
if (credentials == null) {
127-
throw AssertionError("Not logged in");
128+
throw CredentialsException("Not logged in");
128129
}
129130
final uri = credentials.endpointUri('write-checkpoint2.json');
130131

@@ -139,7 +140,7 @@ class StreamingSyncImplementation {
139140
}
140141
}
141142
if (response.statusCode != 200) {
142-
throw getError(response);
143+
throw _getError(response);
143144
}
144145

145146
final body = convert.jsonDecode(response.body);
@@ -237,7 +238,8 @@ class StreamingSyncImplementation {
237238
} else if (line is StreamingSyncCheckpointDiff) {
238239
// TODO: It may be faster to just keep track of the diff, instead of the entire checkpoint
239240
if (targetCheckpoint == null) {
240-
throw AssertionError('Checkpoint diff without previous checkpoint');
241+
throw PowerSyncProtocolException(
242+
'Checkpoint diff without previous checkpoint');
241243
}
242244
final diff = line;
243245
final Map<String, BucketChecksum> newBuckets = {};
@@ -322,7 +324,7 @@ class StreamingSyncImplementation {
322324
Stream<Object?> streamingSyncRequest(StreamingSyncRequest data) async* {
323325
final credentials = await credentialsCallback();
324326
if (credentials == null) {
325-
throw AssertionError('Not logged in');
327+
throw CredentialsException('Not logged in');
326328
}
327329
final uri = credentials.endpointUri('sync/stream');
328330

@@ -339,7 +341,7 @@ class StreamingSyncImplementation {
339341
}
340342
}
341343
if (res.statusCode != 200) {
342-
throw await getStreamedError(res);
344+
throw await _getStreamedError(res);
343345
}
344346

345347
// Note: The response stream is automatically closed when this loop errors
@@ -349,33 +351,38 @@ class StreamingSyncImplementation {
349351
}
350352
}
351353

352-
HttpException getError(http.Response response) {
354+
SyncResponseException _getError(http.Response response) {
353355
try {
354356
final body = response.body;
355357
final decoded = convert.jsonDecode(body);
356-
final details = stringOrFirst(decoded['error']?['details']) ?? body;
358+
final details = _stringOrFirst(decoded['error']?['details']) ?? body;
357359
final message = '${response.reasonPhrase ?? "Request failed"}: $details';
358-
return HttpException(message, uri: response.request?.url);
360+
return SyncResponseException(response.statusCode, message);
359361
} on Error catch (_) {
360-
return HttpException(response.reasonPhrase ?? "Request failed",
361-
uri: response.request?.url);
362+
return SyncResponseException(
363+
response.statusCode,
364+
response.reasonPhrase ?? "Request failed",
365+
);
362366
}
363367
}
364368

365-
Future<HttpException> getStreamedError(http.StreamedResponse response) async {
369+
Future<SyncResponseException> _getStreamedError(
370+
http.StreamedResponse response) async {
366371
try {
367372
final body = await response.stream.bytesToString();
368373
final decoded = convert.jsonDecode(body);
369-
final details = stringOrFirst(decoded['error']?['details']) ?? body;
374+
final details = _stringOrFirst(decoded['error']?['details']) ?? body;
370375
final message = '${response.reasonPhrase ?? "Request failed"}: $details';
371-
return HttpException(message, uri: response.request?.url);
376+
return SyncResponseException(response.statusCode, message);
372377
} on Error catch (_) {
373-
return HttpException(response.reasonPhrase ?? "Request failed",
374-
uri: response.request?.url);
378+
return SyncResponseException(
379+
response.statusCode,
380+
response.reasonPhrase ?? "Request failed",
381+
);
375382
}
376383
}
377384

378-
String? stringOrFirst(Object? details) {
385+
String? _stringOrFirst(Object? details) {
379386
if (details == null) {
380387
return null;
381388
} else if (details is String) {
@@ -386,3 +393,63 @@ String? stringOrFirst(Object? details) {
386393
return null;
387394
}
388395
}
396+
397+
/// Attempt to give a basic summary of the error for cases where the full error
398+
/// is not logged.
399+
String _syncErrorMessage(Object? error) {
400+
if (error == null) {
401+
return 'Unknown';
402+
} else if (error is HttpException) {
403+
return 'Sync service error';
404+
} else if (error is SyncResponseException) {
405+
if (error.statusCode == 401) {
406+
return 'Authorization error';
407+
} else {
408+
return 'Sync service error';
409+
}
410+
} else if (error is SocketException) {
411+
return 'Connection error';
412+
} else if (error is ArgumentError || error is FormatException) {
413+
return 'Configuration error';
414+
} else if (error is CredentialsException) {
415+
return 'Credentials error';
416+
} else if (error is PowerSyncProtocolException) {
417+
return 'Protocol error';
418+
} else {
419+
return '${error.runtimeType}';
420+
}
421+
}
422+
423+
class CredentialsException implements Exception {
424+
String message;
425+
426+
CredentialsException(this.message);
427+
428+
@override
429+
toString() {
430+
return 'CredentialsException: $message';
431+
}
432+
}
433+
434+
class PowerSyncProtocolException implements Exception {
435+
String message;
436+
437+
PowerSyncProtocolException(this.message);
438+
439+
@override
440+
toString() {
441+
return 'SyncProtocolException: $message';
442+
}
443+
}
444+
445+
class SyncResponseException implements Exception {
446+
int statusCode;
447+
String description;
448+
449+
SyncResponseException(this.statusCode, this.description);
450+
451+
@override
452+
toString() {
453+
return 'SyncResponseException: $statusCode $description';
454+
}
455+
}

0 commit comments

Comments
 (0)