{"record":{"id":"107613e0487e8332","repo":"ruby-concurrency/concurrent-ruby","slug":"no-block-given-107613","errorCode":null,"errorMessage":"no block given","messagePattern":"no block given","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/promises.rb","lineNumber":1724,"sourceCode":"\n      # @return [true,false] if resolvable\n      def resolvable?(countdown, future, index)\n        countdown.zero?\n      end\n\n      def process_on_blocker_resolution(future, index)\n        @Countdown.decrement\n      end\n\n      def on_resolvable(resolved_future, index)\n        raise NotImplementedError\n      end\n    end\n\n    # @abstract\n    class BlockedTaskPromise < BlockedPromise\n      def initialize(delayed, blockers_count, default_executor, executor, args, &task)\n        raise ArgumentError, 'no block given' unless block_given?\n        super delayed, 1, Future.new(self, default_executor)\n        @Executor = executor\n        @Task     = task\n        @Args     = args\n      end\n\n      def executor\n        @Executor\n      end\n    end\n\n    class ThenPromise < BlockedTaskPromise\n      private\n\n      def initialize(delayed, blockers_count, default_executor, executor, args, &task)\n        super delayed, blockers_count, default_executor, executor, args, &task\n      end\n","sourceCodeStart":1706,"sourceCodeEnd":1742,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/promises.rb#L1706-L1742","documentation":"BlockedTaskPromise (promises.rb:1723) is the base class of the task-carrying chain steps — ThenPromise, RescuePromise, EnsurePromise, created by Future#then, #rescue, and #ensure. Each requires a block; building the chain step without one raises ArgumentError 'no block given' immediately at chain-construction time, before anything executes.","triggerScenarios":"future.then with no block (missing { }); future.rescue without a block when the author only wanted failure propagation; conditional chaining like cond ? f.then { |v| v } : f.then where one branch lost its block.","commonSituations":"Refactors that delete or move block bodies; intending a no-op pass-through; assuming then merely schedules execution of the upstream future.","solutions":["Supply the transformation block: f.then { |v| ... }.","For an intentional no-op pass-through use f.then { |v| v }.","If you only want to trigger/await execution, use f.wait, f.value, or f.run instead of then."],"exampleFix":"# before\nf2 = f.then # ArgumentError: no block given\n\n# after\nf2 = f.then { |v| v } # explicit pass-through","handlingStrategy":"validation","validationCode":"def then_or_noop(future, &block)\n  block ? future.then(&block) : future.then { |v| v }\nend","typeGuard":null,"tryCatchPattern":null,"preventionTips":["then, rescue, and ensure always require a block; lint for bare calls.","Use f.then { |v| v } for intentional pass-through.","Use f.wait/f.value/f.run when you only need to trigger evaluation, not transform."],"tags":["ruby","concurrency","promises","missing-block","argumenterror"],"backgroundTag":"missing-block-argument","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}