{"record":{"id":"438df7b9031db8c7","repo":"egametang/ET","slug":"add-cancel-token-is-null","errorCode":null,"errorMessage":"add cancel token is null","messagePattern":"add cancel token is null","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.core/Scripts/Core/Share/ETCancellationToken/ETCancellationTokenHelper.cs","lineNumber":44,"sourceCode":"            }\n\n            // 这里用了Fiber.Instance是因为我知道有什么后果, 你们千万不能乱用这个Fiber.Instance\n            await Fiber.Instance.Root.TimerComponent.WaitAsync(afterTimeCancel);\n            if (self.IsCancel())\n            {\n                return;\n            }\n            self.Cancel();\n        }\n        \n        /// <summary>\n        /// 增加一个canceltoken，可以用新增的canceltoken取消协程，当然也可以用老的取消\n        /// </summary>\n        public static async ETTask AddCancel(this ETTask task, ETCancellationToken addCancelToken)\n        {\n            if (addCancelToken == null)\n            {\n                throw new Exception(\"add cancel token is null\");\n            }\n            ETCancellationToken cancelToken = await ETTask.GetContextAsync<ETCancellationToken>();\n            \n            cancelToken?.Add(addCancelToken.Cancel);\n            \n            await task.NewContext(addCancelToken);\n        }\n        \n        /// <summary>\n        /// 增加一个canceltoken，可以用新增的canceltoken取消协程，当然也可以用老的取消\n        /// </summary>\n        public static async ETTask<T> AddCancel<T>(this ETTask<T> task, ETCancellationToken addCancelToken)\n        {\n            if (addCancelToken == null)\n            {\n                throw new Exception(\"add cancel token is null\");\n            }\n            ","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.core/Scripts/Core/Share/ETCancellationToken/ETCancellationTokenHelper.cs#L26-L62","documentation":"Thrown by ETCancellationTokenHelper.AddCancel(this ETTask, ETCancellationToken) when addCancelToken is null. The extension chains a new cancellation source onto the task's context, so a null token cannot be wired up and is rejected up front with a plain Exception. This is the ET coroutine framework's precondition guard.","triggerScenarios":"Calling task.AddCancel(null); passing a token field that was never assigned; reusing a token after it was disposed and set to null; calling the overload that creates a token implicitly but forwarding null by mistake.","commonSituations":"Forgetting to construct ETCancellationToken before calling AddCancel/TimeoutAsync; refactors that removed token creation; conditional code paths that pass null when no timeout is desired (should skip the call instead).","solutions":["Construct the token before calling: var token = new ETCancellationToken(); await task.AddCancel(token);","For no-cancel cases, simply await the task directly instead of calling AddCancel(null).","Prefer the TimeoutAsync(this ETTask, long afterTimeCancel) overload which creates the token internally."],"exampleFix":"// before\nawait task.AddCancel(null); // throws\n\n// after\nawait task.AddCancel(new ETCancellationToken());","handlingStrategy":"validation","validationCode":"// Construct the token before calling AddCancel.\nif (addCancelToken == null) addCancelToken = new ETCancellationToken();\nawait task.AddCancel(addCancelToken);","typeGuard":"static bool IsValidToken(ETCancellationToken t) => t != null && !t.IsDispose();","tryCatchPattern":null,"preventionTips":["Prefer TimeoutAsync(task, afterTimeCancel) which creates the token for you.","Initialize token fields at construction, never pass null.","Skip AddCancel entirely when no cancellation is needed."],"tags":["ettask","etcancellationtoken","argument-validation","coroutine","csharp","unity"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}