{"record":{"id":"55f33386ee4e0a30","repo":"instructure/canvas-lms","slug":"instfs-file-deletion-failed-for-attachment-id-e-message","errorCode":null,"errorMessage":"InstFS file deletion failed for attachment #{id}: #{e.message}","messagePattern":"InstFS file deletion failed for attachment #(.+?): #(.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/models/attachment.rb","lineNumber":2005,"sourceCode":"    end\n    save! if changed?\n    p.workflow_state = \"restored\"\n    p.save!\n  end\n\n  def dmca_file_removal\n    destroy_content_and_replace\n  end\n\n  def destroy_content\n    raise \"must be a root_attachment\" if root_attachment_id\n    return unless filename\n\n    if instfs_hosted?\n      begin\n        InstFS.delete_file(instfs_uuid)\n      rescue InstFS::DeletionError => e\n        Rails.logger.warn(\"InstFS file deletion failed for attachment #{id}: #{e.message}\")\n      end\n      self.instfs_uuid = nil\n    elsif Attachment.s3_storage?\n      s3object.delete unless ApplicationController.test_cluster?\n    else\n      FileUtils.rm_f(full_filename)\n    end\n  end\n\n  def destroy_permanently_plus\n    unless root_attachment_id\n      make_childless\n      destroy_content\n    end\n    destroy_permanently!\n  end\n\n  def make_childless(preferred_child = nil)","sourceCodeStart":1987,"sourceCodeEnd":2023,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/attachment.rb#L1987-L2023","documentation":"When destroying an Attachment hosted in InstFS, Canvas calls InstFS.delete_file; if that raises InstFS::DeletionError, the error is logged with the attachment id and message, and deletion of the DB record proceeds with instfs_uuid cleared — the orphaned InstFS file may remain in storage.","triggerScenarios":"Destroying an attachment whose instfs_hosted? is true when InstFS.delete_file fails: InstFS service unreachable, uuid already deleted, or the service returning an error response.","commonSituations":"InstFS down or misconfigured (host/JWT secret mismatch between Canvas and InstFS); duplicate destroy attempts where the UUID was already removed; network partition between Canvas and InstFS.","solutions":["Check InstFS service health and connectivity from Canvas (instfs host config, shared JWT secret)","Verify the uuid still exists in InstFS — already-deleted files may raise DeletionError","Retry after InstFS recovers, or manually clean the orphaned object in InstFS storage","Note the record is still destroyed and instfs_uuid nil'ed; only orphan cleanup is impacted"],"exampleFix":"// before\nInstFS.delete_file(instfs_uuid)\n// after\nbegin\n  InstFS.delete_file(instfs_uuid)\nrescue InstFS::DeletionError => e\n  Rails.logger.warn(\"InstFS file deletion failed for attachment #{id}: #{e.message}\")\n  # optionally enqueue a retry for orphan cleanup\nend","handlingStrategy":"try-catch","validationCode":"if attachment.instfs_hosted? && attachment.instfs_uuid.present?\n  # proceed with InstFS delete path\nend","typeGuard":"def deletable_attachment?(att)\n  att.persisted? && att.instfs_hosted? && att.instfs_uuid.present?\nend","tryCatchPattern":"begin\n  InstFS.delete_file(instfs_uuid)\nrescue InstFS::DeletionError => e\n  Rails.logger.warn(\"InstFS deletion failed for #{instfs_uuid}: #{e.message}\")\n  # enqueue retry for orphan cleanup\nend","preventionTips":["Monitor InstFS service health and alert on outages","Make deletion idempotent — tolerate already-deleted UUIDs","Queue retry jobs for failed deletions instead of relying only on the log","Verify instfs host and JWT secret config match between Canvas and InstFS"],"tags":["instfs","file-storage","cleanup","resilience"],"backgroundTag":"file-delete-failed","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"}