{"record":{"id":"2c5e72fc68362a2d","repo":"ruby-concurrency/concurrent-ruby","slug":"environment-has-to-be-a-class-inheriting-from-envi","errorCode":null,"errorMessage":"environment has to be a class inheriting from Environment or a module","messagePattern":"environment has to be a class inheriting from Environment or a module","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby-edge/concurrent/edge/erlang_actor.rb","lineNumber":671,"sourceCode":"        super()\n        @Mailbox                = mailbox\n        @Pid                    = Pid.new self, name\n        @Linked                 = ::Set.new\n        @Monitors               = {}\n        @Monitoring             = {}\n        @MonitoringLateDelivery = {}\n        @Terminated             = Promises.resolvable_future\n        @trap                   = false\n        @reply                  = nil\n\n        @Environment = if environment.is_a?(Class) && environment <= Environment\n                         environment.new self, executor\n                       elsif environment.is_a? Module\n                         e = Environment.new self, executor\n                         e.extend environment\n                         e\n                       else\n                         raise ArgumentError,\n                               \"environment has to be a class inheriting from Environment or a module\"\n                       end\n      end\n\n      def tell_op(message)\n        log DEBUG, @Pid, told: message\n        if (mailbox = @Mailbox)\n          mailbox.push_op(message).then { @Pid }\n        else\n          Promises.fulfilled_future @Pid\n        end\n      end\n\n      def tell(message, timeout = nil)\n        log DEBUG, @Pid, told: message\n        if (mailbox = @Mailbox)\n          timed_out = mailbox.push message, timeout\n          timeout ? timed_out : @Pid","sourceCodeStart":653,"sourceCodeEnd":689,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby-edge/concurrent/edge/erlang_actor.rb#L653-L689","documentation":"An Edge ErlangActor gets its message-handling behaviour from an `environment:` argument (keyword on `Concurrent::ErlangActor.spawn`, default `ErlangActor::Environment`). AbstractActor#initialize accepts exactly two shapes: a Class inheriting from ErlangActor::Environment (instantiated with `(actor, executor)`), or a Module that is `extend`ed onto a fresh Environment. Anything else — an instance, String, Symbol, or unrelated object — raises this ArgumentError at spawn time, before any message is processed.","triggerScenarios":"`Concurrent::ErlangActor.spawn(:on_thread, environment: MyEnv.new)` — an instance instead of the class. Passing a String/Symbol constant name (`environment: 'MyEnv'`). Passing a class that does not subclass ErlangActor::Environment (duck-typed behaviour class). Works: `environment: MyEnv` where `MyEnv < ErlangActor::Environment`, or `environment: MyBehaviourModule` (any Module).","commonSituations":"Porting Erlang/Elixir GenServer code where you naturally have behaviour instances; forgetting `< ErlangActor::Environment` when defining the environment class; environment selected from config/strings and passed through without constantizing.","solutions":["Define the environment as a class: `class MyEnv < Concurrent::ErlangActor::Environment; ...; end` and pass `MyEnv` itself (not an instance).","Or keep the callbacks in a plain Module and pass the module — it is extended onto a default Environment instance.","If you already hold an instance, pass its class only when it subclasses Environment; otherwise refactor the behaviour into one of the two supported shapes.","When the environment comes from config, constantize and type-check it at boot, not at spawn time."],"exampleFix":"// before\nclass MyEnv\n  def on_message(msg); reply msg * 2; end\nend\nConcurrent::ErlangActor.spawn(:on_thread, environment: MyEnv.new)\n\n// after\nclass MyEnv < Concurrent::ErlangActor::Environment\n  def on_message(msg); reply msg * 2; end\nend\nConcurrent::ErlangActor.spawn(:on_thread, environment: MyEnv)","handlingStrategy":"type-guard","validationCode":"raise ArgumentError, 'environment must be an Environment subclass or Module' unless erlang_environment?(env)\nConcurrent::ErlangActor.spawn(:on_thread, environment: env) { ... }","typeGuard":"def erlang_environment?(env)\n  (env.is_a?(Class) && env <= Concurrent::ErlangActor::Environment) || env.is_a?(Module)\nend","tryCatchPattern":null,"preventionTips":["Define environments as Environment subclasses or plain modules — never instances.","Funnel actor creation through one factory that type-checks the environment.","Add a boot-time smoke test spawning one actor per environment class."],"tags":["concurrent-ruby","concurrent-ruby-edge","erlang-actor","environment","argumenterror"],"backgroundTag":"invalid-argument-type","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}