Search Unity

  1. Are you interested in providing feedback directly to Unity teams? Sign up to become a member of Unity Pulse, our new product feedback and research community.
    Dismiss Notice

[FIXED] Coroutine issue : coroutine stopped for no reason

Discussion in 'Scripting' started by DoomSamurai, May 29, 2015.

  1. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    Hey all,

    I have a weird issue with coroutines that stop for no reason. I found out that similar issues were discussed here as far back as 2012 : http://answers.unity3d.com/questions/277772/what-can-cause-a-coroutine-to-not-being-working.html

    My problem is that I have a script that starts a coroutine that handles the execution over multiple frames of a character skill. The coroutine usually works fine. In some game conditions (that should have no effect at all in any way on the skill script) the skill stops working. We found out that the coroutine stops even with no calls to StopCoroutine or StopAllCoroutines. I'm positive that we have no code at all stopping the coroutine explicitely or disabling the gameObject that has the skill component. I even tried running the coroutine on another GameObject (otherObject.StartCoroutine(this.myCoroutine())).

    I'm getting pretty frustrated by this bug considering there is no dependency between my skill class and the classes that handles the game state that seems to be triggering the bug, and there are no calls to stop the coroutine anyway.

    If anyone's got any idea or knows if this has been logged to the issue tracker and is known about by the Unity people, let me know. Or if you guys have any idea of what I could check out in my code...

    Edit : the issue was caused by an Invoke("method", time) call with a time value that was a NaN value. Doing this eventually caused all coroutines to stop and break all subsequent coroutines. If you run through a similar issue, check your Invoke calls.
     
    Last edited: Jun 4, 2015
  2. SubZeroGaming

    SubZeroGaming

    Joined:
    Mar 4, 2013
    Posts:
    1,008
    You could start by posting your coroutine...
     
    Ryiah and Deleted User like this.
  3. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    I can but it won't give you any pointer as to why the coroutine stops. The issue happens outside of the coroutine itself. The coroutines goes up to the first yield statement, and then it is never entered again. The coroutine is stopped, the same way it would be if I had deactivated the gameObject it's running on via another script. But it's not the case that the gameObject was deactivated. And I didn't call either StopCoroutine or StopAllCoroutines.

    The following code works for most of the game, until it doesn't. We can reproduce the bug in a variety of ways but every single thing we activate or deactivates in the game state that can trigger the bug has absolutely no link to the skill script. When the coroutine doesn't work, it never passes the first yield statement. This code is simplified but what I removed is a bunch of bool variables being sate appropriately and stuff like that

    Code (CSharp):
    1.  
    2. //coroutine in the base class
    3. private IEnumerator Cast()
    4.     {
    5.         yield return new WaitForSeconds(CastingTime.value);
    6.  
    7.         Character.WithdrawMana();
    8.         Character.OnSkillCast(this);//fire an event
    9.  
    10.         transform.rotation = Quaternion.Euler(0, Angle, 0);
    11.  
    12.         Coroutine currentdocastcoroutine = MasterController.instance.StartCoroutine(DoCast());//coroutine in the derived class
    13.  
    14.         yield return currentdocastcoroutine;
    15.  
    16.         if (canStartCoolDown)
    17.             StartCoolDown();
    18.  
    19.         yield return new WaitForSeconds(AfterCastDelay.value);
    20.  
    21.         IsCasting = false;
    22.  
    23.     }
    edit : Also, if I comment out the first yield statement, in case the CastingTime value is super large, the coroutine goes up to the second yield statement and then is stopped in the same way.
     
  4. Deleted User

    Deleted User

    Guest

    So if I understand correctly, your coroutine goes up to line 14 then (sometimes) stops there?
    What is your "DoCast" Coroutine?
     
  5. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    938
    Maybe someone has experienced exactly this and will be able to give you advice.

    Otherwise, I get the feeling this will be a really tough one to crack without a repro. Can you simplify your scenario and teach us how/provide script to reproduce the problem?
     
    Bunny83 likes this.
  6. A.Killingbeck

    A.Killingbeck

    Joined:
    Feb 21, 2014
    Posts:
    483
    No errors or warnings in the console?
     
  7. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    The DoCast coroutine is an empty virtual IEnumerator. Classes that derive from my abstract skill class can implement their own logic there to instantiate objects and such. This coroutine isn't an issue I can easily add a debug line in the while loop to check if the DoCast is running forever but that is not the case.

    @eisenpony : It is a tough one for sure. I can't walk you through all the scenarios that can reproduce the bug as that would mean showing the code for a LOT of objects. The main thing though is : none of these objects do anything with the skills! It is uncanny! For example, setting a Vector3 property in one of my skill's projectile will cause the projectile to call a delegate upon arrival. The update function checks the distance every frame and calls the delegate when distance < 0.1f. This code is fairly simple but will cause a skill's Cast coroutine to stop for no reason AT SOME POINT later in the game. We can fix the issue but we have no idea why removing the Vector3 property fixes the issue. The projectile doesn't even do anything about the skill beyong calling the destination reached delegate!

    @A.Killingbeck no warnings or errors. We found that the coroutine was stopped by noticing the IsCasting bool is never set to false and then adding a bunch of debugs in the coroutine itself to see when it stops working. The answer is : at some point in the game, the coroutine will never go beyong the first yield statement in the coroutine.
     
  8. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    The bug happens when our game transitions from team deathmatch to free-for-all but removing the code that does this transition, the bug still happens.

    I think there's a ghost in the machine. Crazy bug.
     
  9. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    I'm at the point where I might just try an alternative to coroutines to check whether the bug happens or not. If anyone knows of a good library for implementing coroutine-like tasks, I could try that.
     
  10. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,517
    Does the bug persist if you yield the raw IEnumerator from DoCast?

    Code (csharp):
    1.  
    2. IEnumerator cast = DoCast();
    3. while (cast.MoveNext())
    4.     yield return cast.Current;
    5.  
     
  11. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    I'll try it after my lunch but at first we were using the method name as a string to start and stop the coroutine and the bug was still happening.
     
  12. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    Some progress was made, apparently creating a new script for the skill and copy/pasting the exact same code from the skill that triggered the bug solved the issue. The original script was created on a Mac while all the other skills who don't seem to trigger the bug, including the copy/pasted script, were created on a Windows machine. Is it possible that some character encoding or something that has to do with the Mac screwed up the coroutine execution ?
     
  13. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    938
    I sincerely hope not.
     
  14. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    Nope, reproduced the bug again -_-
     
  15. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    More info : all coroutines seem to have stopped, not just the skills. How can all coroutines even be stopped? Is it possible that a coroutine's yield cause Unity to throw a hidden exception and just stop calling all coroutines?

    Edit: Once the bug happens, any coroutine that we try to start subsequentely will not get past the first yield statement
     
    Last edited: May 29, 2015
    EZaca likes this.
  16. Deleted User

    Deleted User

    Guest

    Are you sure you don't have any Stop(All)Coroutine calls anywhere? Are you switching scenes by chance?
     
  17. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    Yeah I'm positive, the coroutine doesn't stop because of a StopCoroutine, StopAllCoroutine or GameObject.SetActive(false) call. The bug occurs on our game server which doesn't switch scene.
     
  18. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    938
    What kinds of things can you do to initiate this weird behavior?
     
  19. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    Sometimes, executing a skill will do it. Sometimes we have to change from team deathmatch to free for all, sometimes its just a matter of running around in the scene and waiting a few minutes. It's nothing consistent. The only thing that is consistent is the effect : all coroutines stop and no coroutines can be started again.
     
  20. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    938
    I was hoping for some code but I guess you haven't narrowed it down.
     
  21. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    Honestly, I'd like to post code, but I would'nt know what to post. There's a dozen different ways to make the bug happen and none of them implicates an object that would access a skill object, stop a coroutine or deactivate a gameObject on which a skill is attached. There isn't a kill switch in the API to turn off all coroutines across the application a developper here could've called either so I don't know how that could've hapenned from code we wrote unless the Unity internals break from an invalid yield or something.

    Posting code would mean for you to look at my complete game server and I don't think that would be helpful as that's a lot of code.

    I'm hoping a Unity person sees this thread and can give me some pointers as to what could cause all coroutines across the application to stop and also prevent any subsequent coroutine to execute beyond the first yield statement.
     
  22. Deleted User

    Deleted User

    Guest

    So all those scenarios you've given,they're not guaranteed to trigger the bug, are they? It could happen or it could not happen?
    Also you said all coroutines are stopped, not just the one from the code snippet you've showed us but yet it seems like you think that snippet is the cause of it?
    Have you also tried making a new scene with just the scripts that need to be involved in the functioning of the method above and see what happens?
     
  23. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    Scenarios depend on the game configuration. If we remove the rules for managing team assignation for team deathmatch and transition to free-for-all, the bug occurs when casting a skill. Else it appears around the transition from team deathmatch to free-for-all. There's other permutations of game state that causes the bug to occur but they do not trigger it in a way that we can pinpoint. All we can pinpoint for sure is that all our coroutines have stopped and the subsequent ones do not work.
    Actually, the snippet of code is the problem in the sense that our players are locked in IsCasting == true, which prevents them from doing anything else. The root of the problem is that my coroutine was stopped by an external entity of the system but we don'T have an entity that has code that could stop all coroutines across the application so we're just "wuuuuuuuuuuuut?"

    That's kinda what we did by removing all game state modifiers like "Free for all" and "Team Deathmatch" and mission objectives but the bug still occurs.


    I can't know for sure that we're not doing anything wrong but I can say for sure, we have nothing in our code that can stop all the coroutines across the application. I figure something is breaking the engine that runs coroutines in the Unity engine code. That's my best guess :S
     
  24. Deleted User

    Deleted User

    Guest

    Well it sounds like you're gonna have to do some intense debugging on your side.
    Even if it's a Unity bug, you'll probably want to (try to) get more information as to what causes it occur before submitting it as a bug report.
    I guess for now maybe try to find a work-around or attach a debugger to it to find out more info.
     
  25. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    What exactly happens in DoCast?
     
  26. NomadKing

    NomadKing

    Joined:
    Feb 11, 2010
    Posts:
    1,461
    Since you said you've managed to repro the behaviour in multiple ways, I'd stick a debug line before every line of code inside that pesky little SOB, and see if it stops at the same point every time from the different repro's.
     
  27. A.Killingbeck

    A.Killingbeck

    Joined:
    Feb 21, 2014
    Posts:
    483
    Are you modifying Time.timescale?
     
    Zipe92 likes this.
  28. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    Nope, we do not change time scale in our game. Good idea though.

    I'll be trying to pinpoint what exactly can cause this issue today.
     
  29. DoomSamurai

    DoomSamurai

    Joined:
    Oct 10, 2012
    Posts:
    159
    We found out that there was a specific code path that would call Invoke("method", time) with a time that was a NaN (because of an erroneous divide by zero operation). That would not break the game at once but eventually it would stop all coroutines and no coroutines would work afterward. I figure our erroneous code breaks something inside of Unity. We fixed the code to check that the division is valid, which solves the issue.
     
    kyuskoj, tcz8, NomadKing and 2 others like this.
  30. NomadKing

    NomadKing

    Joined:
    Feb 11, 2010
    Posts:
    1,461
    I bet there was some celebration when you finally found out what was going on! :)
     
    tcz8, Caffeen and DoomSamurai like this.
  31. Tom-Mensink

    Tom-Mensink

    Joined:
    Jul 27, 2017
    Posts:
    15
    For those stumbling on this post and having the same problem, there is another cause: When you try to start a coroutine from a disabled gameobject, it will fail with an error message. However ... when you disable the gameobject after starting the coroutine, yet within the same frame, it will also stop the coroutine, without error message!
     
  32. valtver

    valtver

    Joined:
    Nov 19, 2014
    Posts:
    1
    Had the same issue with no exceptions and even debugger was working with no issues:




    Some Method() {
    StartCoroutine(CR3());
    }

    CR1() {
    //blah-blah
    yield return StartCoroutine(CR2());
    Some Method();
    }

    CR2() {
    yield return null;
    }

    CR3() {
    yield return StartCoroutine(CR4());
    yield return StartCoroutine(CR5()); //doesn't get here
    }

    CR4() {
    AsyncOperation ao = Resources.UnloadUnusedAssets();
    while ( !asyncUnloadAssets.isDone) {
    //Async is getting dead here after 1 loop;
    }
    }

    StartCoroutine(CR1);



    1. Issue solved after I split coroutine callstack with regular calls/delayed events triggering.
    2. Issue solved when
    yield return StartCoroutine(SomeMethod())
    in CR1() and
    yield return StartCoroutine(CR3());
    making SomeMethod() an IEnumerator.

    Verdict: Don't mess with nested coroutines so much.
     
  33. kyuskoj

    kyuskoj

    Joined:
    Aug 28, 2013
    Posts:
    44
    I had a problem with this. (2019.4.8)
    Coroutine just stopped when I clicked any enum popup button in the inspector. (There were no error messages.)
    I don't know why, but reopening the unity editor fixes the problem.
    It seems like a NaN Time problem that DoomSamurai talked about.
     
  34. fwalker

    fwalker

    Joined:
    Feb 5, 2013
    Posts:
    181
    I am seeing this same problem on 2019.4.10
    It would be easy to track down :(
     
  35. EpicWolffe

    EpicWolffe

    Joined:
    May 24, 2016
    Posts:
    4
    Had the same problem just now. Turns out I had called StopAllCoroutines() in a previous menu. Silly me.
     
  36. EpicWolffe

    EpicWolffe

    Joined:
    May 24, 2016
    Posts:
    4
    Turns out if you start a coroutine from an object your about to disable it happens as well. This got me through thanks Tom =)
     
  37. Zardify

    Zardify

    Joined:
    Jul 15, 2015
    Posts:
    8
    Having the same issue here... no solution yet. Small codebase, 0 coroutine-stopping. I have a breakpoint at every single possible exit from the coroutine but it - correctly - goes to a fallback "waiting" line "yield return null;" and never gets called again. So, it doesn't even get to executing any code...

    Edit (ignore this one, look below):
    Here's something interesting, which might be an actual workaround for this issue:
    I thought, let's see what happens if I handle the coroutine completely manually, that is, run the IEnumerator by myself. It's not the exact same thing of course, but seems enough for me. It could probably be made the exact thing pretty easily (eg. start and handle multiple "instances").

    Instead of StartCoroutine(...) I set a bool to true, which then signals the next frame's Update to start with executing the coroutine.

    At the Update(...) loop's beginning, I have the following:
    Code (CSharp):
    1. if (this.runRoutine == true)
    2. {
    3.     var routine = this.MyCoroutine();
    4.     this.runRoutine = routine.MoveNext();
    5. }
    If anyone has some info on why this works and the coroutine doesn't, or could explain the differences between this and a real Coroutine (some of which I could theorize), it would be welcome.

    Edit:
    I don't know how I didn't see this (probably because even this is enough for me...) but the last solution I've provided is really just calling the function each frame because the enumerator is recreated each frame and advanced once. :oops: No context is preserved, eg. local variables always start from first value.

    This is how it should be (a bit closer to Unity's coroutine implementation):
    Instead of StartCoroutine(...):
    Code (CSharp):
    1. this.runRoutine = this.MyCoroutine();
    And in Update(...):
    Code (CSharp):
    1. if (this.runRoutine != null)
    2. {
    3.     if (!this.runRoutine.MoveNext())
    4.         this.runRoutine = null;
    5. }
    or
    Code (CSharp):
    1. if (!(this.runRoutine?.MoveNext() ?? true))
    2. {
    3.     this.runRoutine = null;
    4. }

    One difference I can already tell is that this way, when you start a MyCoroutine, it will "stop" and "replace" the first one. The first will not be called anymore. This is again easy to "correct" though. But for me, I'll stick with a simple function call in the end :D:D:D

    Edit (so sorry for the ever-expanding post):
    If this seems to be enough for you, you probably shouldn't use coroutines in the first place. This won't support Unity's WaitFor... (...) results, which means you can only wait until a condition is met. It's just better to use a normal function call on each frame.
     
    Last edited: Dec 5, 2020
  38. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    278
    I've just had this problem, the function that calls StartCoroutine goes quickly out of scope and it's scene unloaded. But the actual IEnumerator method is a static method that is always available across scenes.
    It seems the scope of the setup determines the lifecycle of the actual coroutine, which is strange because it returns no handles?
     
  39. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    278
    How did you fix the issue? Even if you keep a global handle to the return of the StartCoroutine call, it still kills the coroutine when the object where the StartCoroutine call was made goes out of scope
     
  40. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    934
    @Ziplock9000
    The code which manages the coroutine is inside the component where you start it. Therefore, the coroutine stops somewhere in the middle when that object is disabled or destroyed / unloaded. What you can do is safe that object over the scene switches. To do that, make sure your object is at top level in the hierarchy and call
    GameObject.DontDestroyOnLoad(this.gameObject);
    inside one of its components (like the one where your coroutine is managed).

    I currently also facing randomly stopping coroutines... I will edit this post when I find the cause.
     
  41. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    278
    I've used this in the past, but I find it a horrible Unity hacky solution just to get a once a second heartbeat event across multiple scenes. But because Coroutines and InvokeRepeating both have to originate from a MonoBehaviour, there's no way around that.
    The slight advantage with the former being you can start a coroutine in another class, but you cant InvokeRepeating a method in another class. Neither of which helps in my situation.
     
  42. The_MAZZTer

    The_MAZZTer

    Joined:
    Sep 19, 2019
    Posts:
    2
    Unity 2020.1.16 coroutines seem to be buggy and can break until the editor is restarted. Going in and out of Play mode won't fix it.

    What triggers it for me specifically is trying to use UnityWebRequest to load a file://// url (with four slashes). Trying to load files from network shares which some people claim is possible but I have yet to see it work.

    (I guess I will continue to use my workaround of UnityWebRequest -> HttpListener -> FileStream.)
     
  43. JotaRata

    JotaRata

    Joined:
    Dec 8, 2014
    Posts:
    47
    Help I'm having a similar issue!

    I have all my game backed up in Github I've changed nothing in the code, and suddently all coroutines stop after every yield return intruction.
    The UI stopped working as well, you move an element and the game doesn´t update.
     
  44. abandon_games

    abandon_games

    Joined:
    Sep 1, 2017
    Posts:
    5
    I also ran into this odd behavior of coroutines seemingly being aborted without any reproducible or obvious cause, and after much Googling and head-against-wall-banging I think the lessons to be learned are:
    • keep coroutines focused - they should only contain code that actually needs to be calculated overtime
    • avoid nesting coroutines (including yielding to another coroutine) and having many coroutines actively running at once
    • most importantly, avoid the need to externally stop a coroutine, coroutines should only be terminated by the coroutine itself (this seemed to be crux of the unpredictable behavior I was getting, even though I was following the accepted practices of
      StopCoroutine
      )
    granted, the above may not be possible in all scenarios, but imo it's worth sacrificing a bit of code quality or even functionality to avoid this illusive coroutine "bug"
     
  45. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    1,020
    This seems ill adviced. I have never ever had a coroutine that "seemingly" being aborted. I use coroutines all over the place and they work 100% the way they should. If you are seeing something you did not expect, you may just have the wrong idea how they work :) You may want to read through my coroutine crash course, but that may not be enough to properly understand them.

    While your first point is valid, it's more about the unnecessary overhead a coroutine has and not so much about that they have any kinds of issues.

    Your second point is more arbitrary. While I generally advice people to not start / stop coroutines too often due to their memory and performance overhead, it's perfectly fine to have multiple coroutines running infinitely as long as you don't mess with the wrong things. Though this generally applies to any code.

    Your last point is also not really a good general advice. Yes, if you can't really reason about what the coroutine currently does, I would also not recommend to stop / terminate a coroutine from the outside. This is for the same reason you should not force stop threads externally where such an action could even be more dramatic. However there are many reasonable cases where it's perfectly fine to stop a coroutine from the outside.

    If you read through my coroutine crash course you may realise that coroutines are not black magic and 100% deterministic. If you have issues, you're missing some details. I've even created a coroutine serializer which allows to use reflection to serialize a running IEnumerator to a file and restore it's state the next time you execute your application. Yes, this is more a hack and has specific limitations that should be obvious when you understand how coroutines work. However within the definition everything behaves 100% as expected. I use coroutines for over 10 years in Unity and never had any kind of strange behaviour.

    Coroutines are state machine objects that are stored in MonoBehaviour instances and driven by the coroutine scheduler. Coroutines "die" / are removed when the MonoBehaviour it was started on is disabled or the gameobject that MonoBehaviour is on is deactivated. StopCoroutine does exactly the same thing. It just kicks out the object from the internal list. Everything happens synchronously on the main thread, so no spooky actions involved.

    Because coroutines built out of C# iteraters are just ingeneous, I actually implemented my own scheduler in the game SpaceEngineers just within the ingame scripting which allows you to create C# scripts. Coroutines are just great to build sequential actions and have suspend conditions interleaved with the actual actions. This makes building complex elevator controls so much easier and more linear.
     
unityunity