instructure/canvas-lms · error

Resource is not closed

Error message

Resource is not closed

What it means

Accessibility::BulkCloseIssuesService#reopen_issues can only reopen a scan that is currently closed. This guard fires when scan.open? is true — the issues were never bulk-closed — so reopening them is a no-op that would incorrectly reset state. It protects the closed_at lifecycle invariant of the accessibility scan.

Solutions

  1. Check scan.open? before scheduling reopen and skip/no-op when it is already open
  2. Track closed_at: only call reopen when scan.closed? (closed_at present)
  3. Make the caller idempotent: catch this error and treat an already-open scan as success

Example fix

// before
service.reopen_issues
// after
service.reopen_issues unless scan.open?
Defensive patterns

Strategy: try-catch

Validate before calling

service.reopen_issues unless scan.open?

Type guard

scan.respond_to?(:open?) && scan.open? == false

Try / catch

begin
  service.reopen_issues
rescue RuntimeError => e
  Rails.logger.info('scan already open') if e.message == 'Resource is not closed'
end

Prevention

When it happens

Trigger: Calling reopen_issues on a scan that was never closed (open_at/closed_at state indicates open); calling reopen twice — the second call hits the already-reopened scan; UI/API double-submit racing with the first reopen that already succeeded.

Common situations: Users clicking an 'undo close' action after the state has already changed; background jobs re-delivering a reopen request; tests or scripts assuming reopen is idempotent.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/cfcf7a6f645e30eb. Report an issue: GitHub.

Appendix: source

Thrown at app/services/accessibility/bulk_close_issues_service.rb:45

  def call
    if close
      close_issues
    else
      reopen_issues
    end
  end

  private

  attr_reader :scan, :user_id, :close

  def close_issues
    scan.bulk_close_issues!(user_id:)
  end

  def reopen_issues
    raise "Resource is not closed" if scan.open?

    # Reset closed status
    scan.update!(closed_at: nil)

    # Trigger a fresh re-scan
    # This will:
    # - Delete all rescannable issues (active + closed)
    # - Scan the resource for current issues
    # - Create new active issues
    # - Update issue_count
    # - Reset closed_at to nil
    Accessibility::ResourceScannerService.call(resource: scan.context)
  end
end

View on GitHub (pinned to 1c9f0bb801)