instructure/canvas-lms · error · RuntimeError
PostPolicy is missing or does not respond to post_manually
Error message
PostPolicy is missing or does not respond to post_manually
What it means
ScheduledPost processing needs the assignment's PostPolicy to decide whether grades are posted manually. This guard fires when the post_policy is nil (no policy record exists for the assignment/course) or the object does not respond to post_manually, so the scheduled post cannot be safely evaluated. It runs after updating the scheduled_post timestamps so the job will not loop forever on a bad record.
Solutions
- Ensure a PostPolicy exists for the course/assignment (e.g. run the backfill/creation path that guarantees every assignment context has one)
- Check the assignment.context resolves to the correct course on the correct shard before processing
- If post_manually is false, the code intentionally skips — verify the policy is the real record, not a nil placeholder
Example fix
// before
unless post_policy&.post_manually
...
end
// after
unless post_policy&.respond_to?(:post_manually)
raise "PostPolicy missing for assignment #{assignment.id}"
end
unless post_policy.post_manually
...
end Defensive patterns
Strategy: validation
Validate before calling
policy = post_policy || assignment.post_policy raise 'missing PostPolicy' unless policy.respond_to?(:post_manually)
Type guard
policy.respond_to?(:post_manually)
Try / catch
begin
ScheduledPost.process(...)
rescue RuntimeError => e
alert_missing_post_policy if e.message.include?('PostPolicy')
end Prevention
- Backfill PostPolicy records for all courses/assignments
- Watch for nil policies in data-cleanup scripts
- Use real records, not bare doubles lacking post_manually, in specs
When it happens
Trigger: A ScheduledPost job runs for an assignment whose course/assignment PostPolicy record is missing (post_policy nil); a stub/mock or mis-shaped post_policy object without a post_manually method is injected in a test or custom code path; data cleanup removed PostPolicy rows while ScheduledPost rows remain.
Common situations: Courses created via API or imports that skipped PostPolicy creation; sharded/multi-tenant environments where the PostPolicy lookup targets the wrong shard and returns nil; specs with double objects that don't implement post_manually.
Related errors
- Quizzes::QuizSubmission.update_scores called on a quiz that…
- A new_id, '# ', referenced an existing # and the # with #…
- A new_integration_id, '#
- A student referenced a non-existent user #
- A user did not pass validation
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/bc8ca80fe9205906.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/scheduled_post.rb:101
forty_minutes_from_now = 40.minutes.from_now
run_time = Time.current
scheduled_posts_to_process = pending_comments_posting(forty_minutes_from_now).or(pending_grades_posting(forty_minutes_from_now))
scheduled_posts_to_process = scheduled_posts_to_process.where(id: ids) if ids.present?
scheduled_posts_to_process
.preload(
:post_policy,
assignment: :context
).find_each do |scheduled_post|
post_policy = scheduled_post.post_policy
assignment = scheduled_post.assignment
course = assignment.context
# If post_manually is false, skip processing this scheduled post
unless post_policy&.post_manually
scheduled_post.update(post_grades_ran_at: run_time, post_comments_ran_at: run_time)
raise "PostPolicy is missing or does not respond to post_manually" if post_policy.nil? || !post_policy.respond_to?(:post_manually)
end
if scheduled_post.should_post_comments_and_grades?
# If both are set to the same time, then call process_assignment_posting only
progress = course.progresses.new(tag: "scheduled_post_assignment_grades_and_comments:#{assignment.id}")
process_assignment_posting(assignment:, progress:, run_at: scheduled_post.post_grades_at)
scheduled_post.update(post_grades_ran_at: run_time, post_comments_ran_at: run_time)
next
end
if scheduled_post.should_post_grades?(forty_minutes_from_now)
progress = course.progresses.new(tag: "scheduled_post_assignment_grades:#{assignment.id}")
process_assignment_posting(assignment:, progress:, run_at: scheduled_post.post_grades_at)
scheduled_post.update(post_grades_ran_at: run_time)
end
next unless scheduled_post.should_post_comments?(forty_minutes_from_now)
View on GitHub (pinned to 1c9f0bb801)