{"record":{"id":"237d928db8bee1c4","repo":"instructure/canvas-lms","slug":"quiz-submission-not-found-in-connection-items","errorCode":null,"errorMessage":"quiz_submission not found in connection items","messagePattern":"quiz_submission not found in connection items","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"app/graphql/patched_array_connection.rb","lineNumber":41,"sourceCode":"  # the index. Unfortunately submission histories are saved through versionable\n  # which returns an array of submission histories that all share the same id,\n  # and active record overrides the `==` method to check for equality based on\n  # said id. When dealing with submissions, change the comparator to look at the\n  # submitted_at time (what submission#submission_history is doing) instead of\n  # by id so cursors don't break.\n  def cursor_for_submission_node(submission)\n    submission_idx = items.find_index { |i| i.submitted_at.to_i == submission.submitted_at.to_i }\n    raise ArgumentError, \"submission not found in connection items\" unless submission_idx\n\n    encode((submission_idx + 1).to_s)\n  end\n\n  def cursor_for_quiz_submission_node(quiz_submission)\n    # Use object identity (not AR `==` which compares by id) since all items\n    # in the array share the same quiz_submission id but represent different\n    # attempts via simply_versioned deserialization.\n    quiz_submission_idx = items.find_index { |i| i.equal?(quiz_submission) }\n    raise ArgumentError, \"quiz_submission not found in connection items\" unless quiz_submission_idx\n\n    encode((quiz_submission_idx + 1).to_s)\n  end\n\n  def cursor_for(item)\n    return cursor_for_submission_node(item) if item.instance_of?(Submission)\n    return cursor_for_quiz_submission_node(item) if item.instance_of?(Quizzes::QuizSubmission)\n\n    super\n  end\nend\n","sourceCodeStart":23,"sourceCodeEnd":53,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/patched_array_connection.rb#L23-L53","documentation":"PatchedArrayConnection computes quiz submission cursors using object identity (equal?) rather than id equality, because all versions of a quiz_submission in submission history share the same id but represent different attempts. If the node is not the exact in-memory object contained in items, this ArgumentError is raised.","triggerScenarios":"Requesting a cursor for a QuizSubmission object that is a different Ruby object instance than the one in items — a re-fetched copy, a deserialized version from simply_versioned, or a node loaded outside this connection's items array.","commonSituations":"Passing a freshly loaded QuizSubmission.find(id) instead of the object from the connection items; mixing objects across shards/requests; constructing quiz submission history nodes manually and then asking this connection for their cursors.","solutions":["Use the exact object instance from the connection's items array when requesting the cursor","Re-fetch the whole connection (nodes + page info) in a single request instead of mixing objects","Avoid re-loading the quiz submission by id; keep a reference to the original item","If building custom nodes, insert them into the connection so identity holds"],"exampleFix":"// before\nconst qs = QuizSubmission.find(id)\nconst cursor = connection.cursorFor(qs) // new object, identity check fails\n// after\nconst qs = connection.items.find(i => i.id === id)\nconst cursor = connection.cursorFor(qs)","handlingStrategy":"validation","validationCode":"const inPage = connection.items.includes(node) // identity check, same as resolver\nif (!inPage) throw new Error('quiz submission object not in this page')","typeGuard":null,"tryCatchPattern":"try {\n  const cursor = connection.cursorFor(node)\n} catch (e) {\n  if (/quiz_submission not found in connection items/.test(e.message)) {\n    node = connection.items.find(i => i.id === node.id) // re-acquire exact instance\n  } else throw e\n}","preventionTips":["Keep references to objects returned by the connection; do not re-find by id","Fetch nodes and page info in one request","Remember cursor matching is object identity, not id equality"],"tags":["graphql","pagination","cursor","argument-error","object-identity"],"backgroundTag":"resource-not-found","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"}