Using rxJava 1.1.3, when throwing an exception in the onCompleted handling, the onErroris not called nor is the error propagated to the calling thread. It simply disappears.
As an example consider the following spock test:
def "when a onCompleted throws an IllegalStateException, it's handled by the onError handler"() {
given:
boolean onErrorCalledFromSubscribe = false
when:
Observable.just(1, 2, 3)
.subscribe(
Actions.empty(),
{ onErrorCalledFromSubscribe = true},
{throw new IllegalStateException("I'm behaving badly")})
then:
noExceptionThrown()
onErrorCalledFromSubscribe == true
}
The above test fails because onErrorCalledFromSubscribe is never called and is false at the end of the test.
Now, I can imagine that once onCompleted is called, onError shouldn't be called because either the one or the other should be called.
However, I wonder how do you handle such situations where a RuntimeException is called in your onCompleted method and you want to handle the error.
Do you simply wrap the onCompleted call in a try-catch and handle the error there?
Just use a try-catch in onCompleted if you think there might be an exception thrown in there.
As I mentioned in the comments, RxJava should not be swallowing exceptions from onCompleted but currently is; still, if you know that there is going to be the possibility of an exception, you should just handle it yourself.
Related
I need to collect only the first value from two emitted by flow.
I have a function that returns flow:
fun myFlow = flow {
try {
emit(localDataSource.fetchData())
} catch(e: Exception) {
// just skip this error
}
emit(remoteDataSource.fetchData(1000, 0))
}
In one special case I need only first emitted value, doesn't matter is it from local cache or remote source.
I tried this one:
fun getRandomFavoriteItem() = myFlow.first().filter { it.score > 7 }.randomOrNull()
But first() invocation always throws
java.lang.IllegalStateException: Flow exception transparency is violated:
Previous 'emit' call has thrown exception kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted, no more elements needed, but then emission attempt of value.
What I've tried:
single() -
java.lang.IllegalArgumentException: Flow has more than one element
take(1).first() -
java.lang.IllegalStateException: Flow exception transparency is violated:
Previous 'emit' call has thrown exception kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted, no more elements needed, but then emission attempt of value
Catch error but it doesn't stop here:
myFlow.catch { e ->
if (e !is IllegalArgumentException) {
throw e
}
}.first().filter { it.score > 7 }.randomOrNull()
My questions are:
What is the point of usage first() if it doesn't work in case of more than 1 emitted values? If I would know that my flow produces only one value I could just use any other terminal operator.
How to avoid those errors and how to collect only first value without adding repeated code?
This isn't an error in first(). It's an error in your flow. You are not permitted to swallow all exceptions in a Flow in the way you have.
Some varying approaches may differ in whether they detect that error, but what you must fix is how you "just skip" all exceptions. Consider catching only the specific exceptions you're concerned about, or at least making sure to catch and rethrow CancellationException or its subclasses.
Lous Wasserman already found the problem, here some more details.
As mentioned in the error message you're also catching the AbortFlowException.
java.lang.IllegalStateException: Flow exception transparency is
violated: Previous 'emit' call has thrown exception
kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted,
no more elements needed, but then emission attempt of value.
You're bascically catching an exception which interferes with the way flows work. The problem is not about the first function.
Since AbortFlowException is internal you cannot access it, but you can access its superclass CancellationException. You need to modify your catch block like this:
try {
emit(localDataSource.fetchData())
} catch (e: Exception) {
if(e is CancellationException) {
throw e
}
}
Now first will work in the way you expect it to.
Edit:
A better solution would be to handle the exception within fetchData (you might return null in case one was thrown). This way you don't get in the way of the flow mechanics.
If that is not possible, you could create a wrapper function which takes care of the exception handling.
I have a dilemma about handling exceptions in kotlin and coroutines. Will be thankful for any articles or your personal experience.
https://kotlinlang.org/docs/exceptions.html#checked-exceptions kotlin documentation says that exceptions were mistake so there are no checked exceptions in kotlin.
If you have multilayer architecture it is pain in the ass to handle an exception from bottom layer at the top one, cause there are no warnings or compile level checks for what exception you should wait for.
As an option you can catch exception asap, wrap it with some Result class and propagate it back as a return value.
return try {
Result.Success(api.call())
} catch(ex: IOException) {
Result.NetworkError
}
This solution works well until you get hands on coroutines with async/await.
If an exception is thrown inside the first async then whole scope becomes dead.
coroutineScope {
val r1 = async { }
val r2 = async { }
r1.await()
r2.await()
}
But if you use solution with return value, then both async will complete. Even if one of them completed with wrapped error. In 99% cases this behavior doesn't make sense.
So I run into situation interesting situation - there are no compile level/lint checks for catching/throwing checked exceptions so I can't use exceptions properly. But if I get rid of exceptions coroutines start act weird.
As per kotest docs: https://github.com/kotest/kotest/blob/master/doc/nondeterministic.md
You can tell eventually to ignore specific exceptions and any others will immediately fail the test.
I want to pass multiple exceptions to eventually that I know would be thrown by my block so that I can explicitly skip them.
Right now I only see a way to pass one, how do I pass more than one exception to eventually to skip it in case the block throws those exceptions?
You may use superclass for all your exceptions like
eventually(200.milliseconds, exceptionClass = RuntimeException::class) {
throw IllegalStateException()
}
or wrap exceptions
eventually(200.milliseconds, exceptionClass = IllegalStateException::class) {
runCatching { throw UnknownError() }
.onFailure { throw IllegalStateException(it) }
}
In 4.4.3 there are no features with collection of Exception
I have a unit test that tests if method throws an exception when condition is present, and method does throws exception as expected.
- (void)testMethodThrowsWhenConditionIsPresent {
XCTAssertThrows([Foo methodWithCondition: condition], #"Condition is true, method should throw exception");
}
Here is the exception source:
- (void)methodWithCondition:(someType)condition {
if (condition) {
[NSException raise: #"condition is true!" format: #"condition is true!"];
}
}
Why does the test stop at the line the exception is thrown? The test does not go on, it stops at that line, when I expect it to continue and return 1 from XCTAssertThrows(), making the test succeed. The test instead stops with Xcode bringing me to the line it was thrown, with a green `Thread 1: breakpoint 1.1' and the debugger appearing in the console.
Why does the test stop when the execution is thrown?
Because you have a breakpoint, which stops execution.
Why, after removing the breakpoint, does my application crash when the exception is thrown?
Because you have an unhandled exception. Unhandled exceptions cause your program to crash.
How can I handle an exception so it won't crash my program?
The easy answer to this question is to simply NOT throw an exception. In other programming languages, like Java, this is perfectly standard. But in Objective-C, we don't really do exceptions. In Objective-C, exceptions should be saved for TRULY exceptional behavior.
With that said, and a strong suggestion for you to find another way to handle whatever it is you're trying to handle, this is how you handle an exception in Objective-C:
#try {
// code that could throw an exception
}
#catch (NSException *e) {
// handle the exception...
}
#finally {
// post try-catch code, executed every time
}
I have the following coding
try
{
var foundCanProperty = properties
.First(x => x.Name == "Can" + method.Name);
var foundOnExecuteMethod = methods
.First(x => x.Name == "On" + method.Name);
var command = new Command(this, foundOnExecuteMethod, foundCanProperty);
TrySetCommand(foundControl as Control, command);
}
catch (InvalidOperationException ex)
{
throw new FatalException("Please check if you have provided all 'On' and 'Can' methods/properties for the view" + View.GetType().FullName, ex);
}
I'd expected that if the methods.First() (in second var statement) throws an InvalidOperationException, I'd be able to catch it. But this seems not be the case (catch block is ignored and the application terminates with the raised exception). If I throw myself an exception of the same type within the try block, it gets caught. Does Linq use multihreading so that the exception is thrown in another thread? Perhaps I make also a stupid error here and just do not see it :(.
Thanks for any help!
I know that this isn't an answer, but rather some additional steps for debugging, but does it change anything if you instead try to catch the general type "Exception" instead of the IOE? That may help to isolate if the method is truly throwing an IOE, or if its failure is generating an IOE somewhere else in the stack. Also - assuming this method isn't in main() - is there a way to wrap the call to it in a try/catch and then inspect the behavior at that point in the call flow?
Apologies, too, in that I know very little about the SilverLight development environment so hopefully the suggestions aren't far fetched.
InvalidOperationException exception occures when The source sequence is empty.
refere to http://msdn.microsoft.com/en-us/library/bb291976.aspx
check weather "properties" or "methods" is not empty.
out of interest, Why you not using FirstOrDefault ?