instructure/canvas-lms · error · GraphQL::ExecutionError
assignment not found: #
Error message
assignment not found: #{assignment_id} What it means
UpdateAssignment#resolve parses the input id and calls Assignment.find(assignment_id). A ActiveRecord::RecordNotFound is caught and converted to GraphQL::ExecutionError 'assignment not found: <id>'. The mutation aborts before any permission check or update is performed.
Solutions
- Re-fetch the assignment id from the course's assignments connection and retry
- Confirm the assignment exists (Assignment.exists?) and is not deleted
- Check you are hitting the correct Canvas shard/account for that id
- If the assignment was deleted, restore it or drop the mutation
Example fix
// before
mutation { updateAssignment(input: {id: "999", name: "New"}) { ... } }
// after
// resolve the id against the course first
query { course(id: 1) { assignmentsConnection { nodes { id name } } } }
mutation { updateAssignment(input: {id: "<existing-id>", name: "New"}) { ... } } Defensive patterns
Strategy: try-catch
Validate before calling
const assignments = await canvasQuery(`query { course(id: $cid) { assignmentsConnection { nodes { id } } } }`, {cid});
if (!assignments?.assignmentsConnection?.nodes.some(a => a.id === inputId)) throw new Error(`Assignment ${inputId} not found in course; aborting update`); Type guard
function isKnownAssignment(node, id) { return node != null && node.id === id; } Try / catch
try {
await updateAssignment({ id });
} catch (e) {
if (/assignment not found/.test(e.message)) {
// refetch assignment list, surface stale-id error to user
} else { throw e; }
} Prevention
- Refresh assignment ids after any delete/merge operations
- Never reuse ids captured in another Canvas environment/shard
- Include the course context in error logs to spot cross-course id use
When it happens
Trigger: Calling the updateAssignment mutation with an id that is not a valid Assignment (deleted assignment, wrong shard/context id, or an id that GraphQLHelpers.parse_relay_or_legacy_id resolved to a non-existent record).
Common situations: Client caches a stale assignment id after the assignment was deleted; tests/scripts hard-code ids from another environment; id confusion between Assignment and its discussion_topic/checkpoint sub-assignments.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/447d19a2065db571.
Report an issue: GitHub.
Appendix: source
Thrown at app/graphql/mutations/update_assignment.rb:34
#
# 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 Mutations::UpdateAssignment < Mutations::AssignmentBase::Mutation
graphql_name "UpdateAssignment"
argument :id, ID, required: true
argument :name, String, required: false
# most arguments inherited from AssignmentBase
def resolve(input:)
assignment_id = GraphQLHelpers.parse_relay_or_legacy_id(input[:id], "Assignment")
begin
@working_assignment = Assignment.find(assignment_id)
rescue ActiveRecord::RecordNotFound
raise GraphQL::ExecutionError, "assignment not found: #{assignment_id}"
end
# check permissions asap
raise GraphQL::ExecutionError, "insufficient permission" unless @working_assignment.grants_right? current_user, :update
update_proxy = ApiProxy.new(context[:request], @working_assignment, context[:session], current_user, in_app: context[:in_app])
# to use the update_api_assignment method, we have to modify some of the
# input. first, update_api_assignment doesnt expect a :state key. instead,
# it expects a :published key of boolean type.
# also, if we are required to transition to restored or destroyed, then we
# need to handle those as well.
input_hash = input.to_h
other_update_on_assignment = false
if input_hash.key? :state
asked_state = input_hash.delete :state
case asked_state
when "unpublished"View on GitHub (pinned to 1c9f0bb801)