{"record":{"id":"4308e6e994992bf3","repo":"Unity-Technologies/UnityCsReference","slug":"deployment-targets-main-thread-context-can-only-be","errorCode":null,"errorMessage":"Deployment targets main thread context can only be retrieved from the main thread.","messagePattern":"Deployment targets main thread context can only be retrieved from the main thread\\.","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"Editor/Mono/DeploymentTargets/DefaultDeploymentTargetsExtension.cs","lineNumber":61,"sourceCode":"\n        internal override void Stop() {}\n\n        internal override void Clear() {}\n    }\n\n    internal abstract class DefaultDeploymentTargetsExtension\n        : IDeploymentTargetsExtension\n    {\n        public virtual IDeploymentTargetsMainThreadContext GetMainThreadContext(bool setup)\n        {\n            CheckGetMainThreadContextCalledOnMainThread();\n            return new DefaultDeploymentTargetsMainThreadContext();\n        }\n\n        protected void CheckGetMainThreadContextCalledOnMainThread()\n        {\n            if (!InternalEditorUtility.CurrentThreadIsMainThread())\n                throw new NotSupportedException(\"Deployment targets main thread context can only be retrieved from the main thread.\");\n        }\n\n        public virtual List<DeploymentTargetIdAndStatus> GetKnownTargets(IDeploymentTargetsMainThreadContext context, ProgressHandler progressHandler = null)\n        {\n            return new List<DeploymentTargetIdAndStatus>();\n        }\n\n        public virtual IDeploymentTargetInfo GetTargetInfo(IDeploymentTargetsMainThreadContext context, DeploymentTargetId targetId, ProgressHandler progressHandler = null)\n        {\n            return new DefaultDeploymentTargetInfo();\n        }\n\n        public virtual DeploymentTargetLogger GetTargetLogger(IDeploymentTargetsMainThreadContext context, DeploymentTargetId targetId)\n        {\n            return new DefaultDeploymentTargetLogger();\n        }\n\n        public virtual IDeploymentLaunchResult LaunchBuildOnTarget(IDeploymentTargetsMainThreadContext context, BuildProperties buildProperties, DeploymentTargetId targetId, ProgressHandler progressHandler = null)","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/Unity-Technologies/UnityCsReference/blob/225b0fbdb57cc17d094e8056b71f8314aba56f73/Editor/Mono/DeploymentTargets/DefaultDeploymentTargetsExtension.cs#L43-L79","documentation":"Thrown by DefaultDeploymentTargetsExtension.GetMainThreadContext via CheckGetMainThreadContextCalledOnMainThread when the call is not on the Unity main thread. Deployment target discovery (ADB device enumeration on Android, etc.) is main-thread-affined because it touches native editor state. NotSupportedException signals that the operation has an inviolable precondition, not a recoverable failure.","triggerScenarios":"Calling any DeploymentTargetManager API that internally calls GetMainThreadContext (e.g. constructing DeploymentTargetManager, GetKnownTargets) from a Task, a ThreadPool callback, or a background thread without marshalling back to the main thread.","commonSituations":"Build pipeline running device deployment steps on a background task; a CI script invoking deployment target enumeration off the main thread; async/await continuation that lost the main thread synchronization context after an await.","solutions":["Marshal the call to the main thread: wrap it in EditorApplication.delayCall or a main-thread dispatcher before invoking the deployment API.","Restructure async code to do the DeploymentTargetManager work synchronously on the main thread and only offload non-Unity work to background tasks.","If you control the entry point, assert InternalEditorUtility.CurrentThreadIsMainThread() at the top of the method that leads here."],"exampleFix":"// before\nawait Task.Run(() => manager.GetKnownTargets());\n// after\nList<DeploymentTargetIdAndStatus> targets = null;\nEditorApplication.delayCall += () => { targets = manager.GetKnownTargets(); };","handlingStrategy":"validation","validationCode":"if (!UnityEngine.InternalEditorUtility.CurrentThreadIsMainThread())\n    throw new InvalidOperationException(\"Must be on main thread\");\n// or marshal:\n// EditorApplication.delayCall += () => DoDeploymentWork();","typeGuard":null,"tryCatchPattern":"try { ctx = ext.GetMainThreadContext(setup); }\ncatch (NotSupportedException ex) when (ex.Message.Contains(\"main thread\"))\n{ /* re-dispatch to main thread via EditorApplication.delayCall */ }","preventionTips":["Assume all DeploymentTargetManager APIs are main-thread-affined.","Do not await between fetching the context and using it.","Assert main-thread at the entry point of any build/deployment routine."],"tags":["editor","deployment","threading","main-thread","android","ios"],"backgroundTag":null,"analyzedSha":"225b0fbdb57cc17d094e8056b71f8314aba56f73","analyzedAt":"2026-08-13T19:07:19.849Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}