instructure/canvas-lms · error · ArgumentError

Cannot include a concatenation in a merge.

Error message

Cannot include a concatenation in a merge.

What it means

BookmarkedCollection::MergeProxy#initialize rejects any collection argument that is a BookmarkedCollection::ConcatProxy. A merge merges per-collection bookmarked pages in parallel, which is semantically undefined for a concatenation (which consumes collections sequentially), so the constructor fails fast with ArgumentError.

Solutions

  1. Restructure so the concatenated collection is materialized as a single bookmarked collection before merging
  2. Use a nested strategy: merge the plain collections, then concatenate the merge result (or vice versa) instead of merging a concat
  3. Flatten the concat's constituent collections and pass each to the merge individually
  4. Write a custom merge_proc over the concat's inner collections directly

Example fix

// before
merged = BookmarkedCollection.merge('a' => concat_proxy, 'b' => coll2)
// after
merged = BookmarkedCollection.merge('a' => base1, 'b' => coll2)
result = BookmarkedCollection.concatenate(merged, other_coll)
Defensive patterns

Strategy: validation

Validate before calling

raise 'concat cannot be merged' if collections.values.any? { |c| c.is_a?(BookmarkedCollection::ConcatProxy) }

Type guard

def mergeable?(coll)
  !coll.is_a?(BookmarkedCollection::ConcatProxy)
end

Try / catch

begin
  merged = BookmarkedCollection.merge(collections)
rescue ArgumentError => e
  Rails.logger.warn("merge rejected: #{e.message}")
  merged = fallback_pipeline(collections)
end

Prevention

When it happens

Trigger: Building BookmarkedCollection.merge(...) where one of the passed collections was produced by BookmarkedCollection.concatenate/concat, e.g. merge('a' => concat_coll, 'b' => bookmarked_coll).

Common situations: Composing pipelines dynamically where a collection may or may not be a concat; passing a concat where a plain bookmarked collection was expected after refactoring collection construction; misunderstanding merge vs concat semantics in Canvas's bookmarked collection gem.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/12e130aa9bfc8042. Report an issue: GitHub.

Appendix: source

Thrown at gems/bookmarked_collection/lib/bookmarked_collection/merge_proxy.rb:24

# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#

class BookmarkedCollection::MergeProxy < BookmarkedCollection::CompositeProxy
  def initialize(collections, &merge_proc)
    if collections.any? { |(_, coll)| coll.is_a?(BookmarkedCollection::ConcatProxy) }
      raise ArgumentError, "Cannot include a concatenation in a merge."
    end

    super(collections)
    @merge_proc = merge_proc
  end

  # a pair of (1) the leaf bookmark of the next value in collections[index] and
  # (2) the index
  def indexed_bookmark(collection, index)
    [collection.leaf_bookmark_for(collection.first), index]
  end

  def execute_pager(pager)
    # decompose current bookmark
    subbookmark, start_index = pager.decompose_bookmark

    # paginate each subcollection
    collections = []

View on GitHub (pinned to 1c9f0bb801)