{"record":{"id":"e3fef4a4919aa44a","repo":"discourse/discourse","slug":"expected-presencechannel-config-or-nil-got-a-r","errorCode":null,"errorMessage":"Expected PresenceChannel::Config or nil. Got a #{result.class.name}","messagePattern":"Expected PresenceChannel::Config or nil\\. Got a #(.+?)","errorType":"exception","errorClass":"PresenceChannel::InvalidConfig","httpStatus":null,"severity":"error","filePath":"lib/presence_channel.rb","lineNumber":323,"sourceCode":"    elsif cached_config\n      Config.from_json(cached_config)\n    else\n      prefix = name[%r{/([a-zA-Z0-9_-]+)/.*}, 1]\n      raise PresenceChannel::NotFound if prefix.nil?\n\n      config_block = @@configuration_blocks[prefix]\n      config_block ||=\n        DiscoursePluginRegistry.presence_channel_prefixes.find { |t| t[0] == prefix }&.[](1)\n      raise PresenceChannel::NotFound if config_block.nil?\n\n      result = config_block.call(name)\n      to_cache =\n        if result.is_a? Config\n          result.to_json\n        elsif result.nil?\n          Config::NOT_FOUND\n        else\n          raise InvalidConfig.new \"Expected PresenceChannel::Config or nil. Got a #{result.class.name}\"\n        end\n\n      DiscourseRedis.ignore_readonly do\n        PresenceChannel.redis.set(redis_key_config, to_cache, ex: CONFIG_CACHE_SECONDS)\n      end\n\n      raise PresenceChannel::NotFound if result.nil?\n      result\n    end\n  end\n\n  def publish_message(entering_user_ids: nil, leaving_user_ids: nil)\n    message = {}\n    if config.count_only\n      message[\"count_delta\"] = entering_user_ids&.count || 0\n      message[\"count_delta\"] -= leaving_user_ids&.count || 0\n      return if message[\"count_delta\"] == 0\n    else","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/discourse/discourse/blob/1b2d7253e854205d2c95c80b2c6a1cdd42e45059/lib/presence_channel.rb#L305-L341","documentation":"Raised in PresenceChannel#fetch_config as PresenceChannel::InvalidMessage when a registered config block returns a value that is neither a PresenceChannel::Config nor nil. Only those two shapes are accepted (nil is cached as the NOT_FOUND sentinel); anything else means a misbehaving channel configuration block. The at-fault input is the return value of the block registered for the channel's prefix.","triggerScenarios":"Thrown at lib/presence_channel.rb:323 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Fix the config block for that prefix to return a PresenceChannel::Config for existing channels or nil for non-existent ones","Check for blocks that accidentally return a string/hash or raise-dependent truthy value","Test the block directly with representative channel names to confirm return types"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"1b2d7253e854205d2c95c80b2c6a1cdd42e45059","analyzedAt":"2026-08-26T03:11:08.302Z","contentChangedAt":"2026-08-26T03:11:08.302Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}