{"record":{"id":"1772433d2169259b","repo":"louthy/language-ext","slug":"2000000009-the-io-monad-is-not-in-the-monad-transformer","errorCode":"-2000000009","errorMessage":"The IO monad is not in the monad-transformer stack or MonadIO.ToIO has not been implemented in the trait implementation for your monad-type.  Therefore it's not possible to leverage `MonadIO` unlifting trait functionality. To resolve this, implement `MonadIO.ToIO` and/or `MonadIO.MapIO`.","messagePattern":"The IO monad is not in the monad-transformer stack or MonadIO\\.ToIO has not been implemented in the trait implementation for your monad-type\\.  Therefore it's not possible to leverage `MonadIO` unlifting trait functionality\\. To resolve this, implement `MonadIO\\.ToIO` and/or `MonadIO\\.MapIO`\\.","errorType":"error_code","errorClass":"ExceptionalException","httpStatus":null,"severity":"error","filePath":"LanguageExt.Core/Traits/Maybe Traits/MonadUnliftIO/MonadUnliftIO.Trait.cs","lineNumber":30,"sourceCode":"    public interface MonadUnliftIO<M> : Maybe.MonadIO<M>\n        where M : MonadUnliftIO<M>, Traits.Monad<M>\n    {\n        /// <summary>\n        /// Extract the IO monad from within the M monad (usually as part of a monad-transformer stack).\n        /// </summary>\n        /// <remarks>\n        /// IMPLEMENTATION REQUIRED: If this method isn't overloaded in this monad\n        /// or any monad in the stack on the way to the inner-monad, then it will throw\n        /// an exception.\n        ///\n        /// This isn't ideal, it appears to be the only way to achieve this\n        /// kind of functionality in C# without resorting to magic. \n        /// </remarks>\n        /// <exception cref=\"ExceptionalException\">If this method isn't overloaded in\n        /// the inner monad or any monad in the stack on the way to the inner-monad,\n        /// then it will throw an exception.</exception>\n        public static virtual K<M, IO<A>> ToIOMaybe<A>(K<M, A> ma) =>\n            throw new ExceptionalException(Errors.ToIONotSupported);\n\n        /// <summary>\n        /// Extract the IO monad from within the `M` monad (usually as part of a monad-transformer stack).  Then perform\n        /// a mapping operation on the IO action before lifting the IO back into the `M` monad.\n        /// </summary>\n        public static virtual K<M, B> MapIOMaybe<A, B>(K<M, A> ma, Func<IO<A>, IO<B>> f) =>\n            M.ToIOMaybe(ma).Bind(io => M.LiftIOMaybe(f(io)));\n\n        /// <summary>\n        /// Queue this IO operation to run on the thread-pool. \n        /// </summary>\n        /// <param name=\"timeout\">Maximum time that the forked IO operation can run for. `None` for no timeout.</param>\n        /// <returns>Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel\n        /// the forked IO operation or to await the result of it.\n        /// </returns>\n        public static virtual K<M, ForkIO<A>> ForkIOMaybe<A>(K<M, A> ma, Option<TimeSpan> timeout = default) =>\n            M.MapIOMaybe(ma, io => io.Fork(timeout));\n","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/louthy/language-ext/blob/2f0e3628242889774d4141960a35671a0280051f/LanguageExt.Core/Traits/Maybe Traits/MonadUnliftIO/MonadUnliftIO.Trait.cs#L12-L48","documentation":"MonadUnliftIO.ToIOMaybe's default implementation throws ExceptionalException (code -2000000009) because the monad M (or its stack) does not implement MonadIO.ToIO/MapIO, so the library cannot extract the IO monad from within M. Unlifting requires explicit support in the trait implementation.","triggerScenarios":"Calling ToIOMaybe<A>(K<M, A>) (or MapIOMaybe) on a monad whose trait impl lacks ToIO/MapIO overrides.","commonSituations":"Trying to run an IO action embedded inside a custom monad or transformer stack without an IO layer; generic monadic code that assumes unlift capability.","solutions":["Override MonadIO.ToIO and/or MonadIO.MapIO in the trait implementation for M","Restructure so the IO monad is the outermost layer and can be run directly","Replace unlift-style code with lift-style code (lift the final effect into M)"],"exampleFix":"// before\nvar io = ToIOMaybe(ma); // ExceptionalException -2000000009\n// after\n// in trait impl for M: public K<M, IO<A>> ToIO<A>(K<M, A> ma) => Map(ma, IO.pure);","handlingStrategy":"try-catch","validationCode":"// static check: MonadIO<M> overrides ToIO/MapIO before calling ToIOMaybe","typeGuard":"bool CanUnliftIO<M>() => typeof(MonadIO<M>).GetMethod(\"ToIO\")!.DeclaringType != typeof(MonadIO<M>);","tryCatchPattern":"try { return ToIOMaybe(ma); }\ncatch (ExceptionalException ex) when (ex.Code == Errors.ToIONotSupported.Code) { /* restructure to lift-style */ }","preventionTips":["Implement ToIO/MapIO when the monad wraps IO","Avoid generic unlift assumptions in monad-polymorphic code","Prefer lifting effects out rather than unlifting IO from within"],"tags":["csharp","languageext","monadio","io"],"backgroundTag":"method-not-implemented","analyzedSha":"2f0e3628242889774d4141960a35671a0280051f","analyzedAt":"2026-09-15T03:31:55.716Z","contentChangedAt":"2026-09-15T03:31:55.716Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}