{"record":{"id":"12e130aa9bfc8042","repo":"instructure/canvas-lms","slug":"cannot-include-a-concatenation-in-a-merge","errorCode":null,"errorMessage":"Cannot include a concatenation in a merge.","messagePattern":"Cannot include a concatenation in a merge\\.","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"gems/bookmarked_collection/lib/bookmarked_collection/merge_proxy.rb","lineNumber":24,"sourceCode":"# This file is part of Canvas.\n#\n# Canvas is free software: you can redistribute it and/or modify it under\n# the terms of the GNU Affero General Public License as published by the Free\n# Software Foundation, version 3 of the License.\n#\n# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY\n# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more\n# details.\n#\n# You should have received a copy of the GNU Affero General Public License along\n# with this program. If not, see <http://www.gnu.org/licenses/>.\n#\n\nclass BookmarkedCollection::MergeProxy < BookmarkedCollection::CompositeProxy\n  def initialize(collections, &merge_proc)\n    if collections.any? { |(_, coll)| coll.is_a?(BookmarkedCollection::ConcatProxy) }\n      raise ArgumentError, \"Cannot include a concatenation in a merge.\"\n    end\n\n    super(collections)\n    @merge_proc = merge_proc\n  end\n\n  # a pair of (1) the leaf bookmark of the next value in collections[index] and\n  # (2) the index\n  def indexed_bookmark(collection, index)\n    [collection.leaf_bookmark_for(collection.first), index]\n  end\n\n  def execute_pager(pager)\n    # decompose current bookmark\n    subbookmark, start_index = pager.decompose_bookmark\n\n    # paginate each subcollection\n    collections = []","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/gems/bookmarked_collection/lib/bookmarked_collection/merge_proxy.rb#L6-L42","documentation":"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.","triggerScenarios":"Building BookmarkedCollection.merge(...) where one of the passed collections was produced by BookmarkedCollection.concatenate/concat, e.g. merge('a' => concat_coll, 'b' => bookmarked_coll).","commonSituations":"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.","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"],"exampleFix":"// before\nmerged = BookmarkedCollection.merge('a' => concat_proxy, 'b' => coll2)\n// after\nmerged = BookmarkedCollection.merge('a' => base1, 'b' => coll2)\nresult = BookmarkedCollection.concatenate(merged, other_coll)","handlingStrategy":"validation","validationCode":"raise 'concat cannot be merged' if collections.values.any? { |c| c.is_a?(BookmarkedCollection::ConcatProxy) }","typeGuard":"def mergeable?(coll)\n  !coll.is_a?(BookmarkedCollection::ConcatProxy)\nend","tryCatchPattern":"begin\n  merged = BookmarkedCollection.merge(collections)\nrescue ArgumentError => e\n  Rails.logger.warn(\"merge rejected: #{e.message}\")\n  merged = fallback_pipeline(collections)\nend","preventionTips":["Track which builder functions return concat proxies","Materialize concats into bookmarked collections before merging","Write unit tests for collection-composition pipelines"],"tags":["ruby","bookmarked-collection","argument-error"],"backgroundTag":"invalid-argument-value","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}