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
- Restructure so the concatenated collection is materialized as a single bookmarked collection before merging
- Use a nested strategy: merge the plain collections, then concatenate the merge result (or vice versa) instead of merging a concat
- Flatten the concat's constituent collections and pass each to the merge individually
- 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
- Track which builder functions return concat proxies
- Materialize concats into bookmarked collections before merging
- Write unit tests for collection-composition pipelines
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
- block is required if module_name is not passed
- cannot pass both a module_name and a block
- Missing required asset parameter #
- Missing required parameter: copied_at
- Missing required parameter: course
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)