instructure/canvas-lms · error · ArgumentError
submission not found in connection items
Error message
submission not found in connection items
What it means
PatchedArrayConnection overrides cursor computation for Submission nodes, matching items by submitted_at timestamp instead of AR id equality (submission history shares ids). If the given submission's submitted_at does not match any item in the connection's items array, it raises this ArgumentError — the node is not part of the page used to build cursors.
Solutions
- Only request cursors for nodes that came from this connection's items in the same request/page
- Re-fetch the connection so items and node are consistent (resubmission changes submitted_at)
- Verify the submission is part of the current page before computing the cursor
- If comparing history versions, ensure the exact serialized version (same submitted_at) is in items
Example fix
// before const cursor = submissionConnection.cursorFor(someSubmission) // node from another page // after const item = submissionConnection.items.find(i => i.id === someSubmission.id) const cursor = item ? submissionConnection.cursorFor(item) : null
Defensive patterns
Strategy: validation
Validate before calling
const inPage = connection.items.some(i => Math.floor(Date.parse(i.submittedAt) / 1000) === Math.floor(Date.parse(node.submittedAt) / 1000))
if (!inPage) throw new Error('submission not on this page; cannot compute cursor') Try / catch
try {
const cursor = connection.cursorFor(node)
} catch (e) {
if (/submission not found in connection items/.test(e.message)) {
await refetchConnection() // node/page drifted (e.g. resubmission)
} else throw e
} Prevention
- Only compute cursors for nodes taken from the same page of items
- Refetch the connection after submissions are resubmitted (submitted_at changes)
- Never fabricate nodes client-side and ask the connection for their cursor
When it happens
Trigger: Requesting a cursor (via cursor_for / pageInfo on a node) for a Submission whose submitted_at.to_i differs from every item in items — the submission belongs to another page of results, was resubmitted so its submitted_at changed after items were loaded, or the node was fabricated client-side.
Common situations: Paginated submission-history queries where a resubmission updates submitted_at between loading items and requesting the cursor; asking for a cursor of a submission outside the current page; custom loaders passing nodes that did not originate from this connection's items array.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- quiz_submission not found in connection items
- Cannot request more than 100 courses at once
- relative entry not found
- and cannot be used together
- and cannot be used together
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/75953bb3ecdb4192.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/patched_array_connection.rb:31
# 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 PatchedArrayConnection < GraphQL::Pagination::ArrayConnection
# The default ArrayConnection uses `find_index(item)` which uses `==` to get
# the index. Unfortunately submission histories are saved through versionable
# which returns an array of submission histories that all share the same id,
# and active record overrides the `==` method to check for equality based on
# said id. When dealing with submissions, change the comparator to look at the
# submitted_at time (what submission#submission_history is doing) instead of
# by id so cursors don't break.
def cursor_for_submission_node(submission)
submission_idx = items.find_index { |i| i.submitted_at.to_i == submission.submitted_at.to_i }
raise ArgumentError, "submission not found in connection items" unless submission_idx
encode((submission_idx + 1).to_s)
end
def cursor_for_quiz_submission_node(quiz_submission)
# Use object identity (not AR `==` which compares by id) since all items
# in the array share the same quiz_submission id but represent different
# attempts via simply_versioned deserialization.
quiz_submission_idx = items.find_index { |i| i.equal?(quiz_submission) }
raise ArgumentError, "quiz_submission not found in connection items" unless quiz_submission_idx
encode((quiz_submission_idx + 1).to_s)
end
def cursor_for(item)
return cursor_for_submission_node(item) if item.instance_of?(Submission)
return cursor_for_quiz_submission_node(item) if item.instance_of?(Quizzes::QuizSubmission)
View on GitHub (pinned to 1c9f0bb801)