{"record":{"id":"f0e6bb1924d4da93","repo":"antiwork/gumroad","slug":"this-commission-has-already-been-completed-so-its","errorCode":null,"errorMessage":"This commission has already been completed, so its files can no longer be changed.","messagePattern":"This commission has already been completed, so its files can no longer be changed\\.","errorType":"validation","errorClass":"ActiveRecord::RecordInvalid","httpStatus":422,"severity":"error","filePath":"app/controllers/commissions_controller.rb","lineNumber":43,"sourceCode":"    begin\n      commission.create_completion_purchase!\n    rescue ActiveRecord::RecordInvalid => e\n      errors = e.record&.errors&.full_messages.presence || [\"Failed to complete commission\"]\n      return render json: { errors: }, status: :unprocessable_entity\n    rescue => e\n      Rails.logger.error(\"Commission #{params[:id]} completion failed: #{e.class}: #{e.message}\")\n      return render json: { errors: [\"Failed to complete commission\"] }, status: :unprocessable_entity\n    end\n\n    head :no_content\n  end\n\n  private\n    def ensure_files_can_be_changed!(commission)\n      return if commission.files_are_editable?\n\n      commission.errors.add(:base, \"This commission has already been completed, so its files can no longer be changed.\")\n      raise ActiveRecord::RecordInvalid, commission\n    end\n\n    def permitted_params\n      params.permit(file_signed_ids: [])\n    end\nend\n","sourceCodeStart":25,"sourceCodeEnd":50,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/controllers/commissions_controller.rb#L25-L50","documentation":"Raised as ActiveRecord::RecordInvalid by CommissionsController#update's ensure_files_can_be_changed! guard when the target commission's files are no longer editable. Editability is defined by Commission#files_are_editable? (app/models/commission.rb:113) as !is_completed? && completion_purchase.nil?, so the lock applies both after completion and while a completion charge is still settling. The controller renders the model's :base error to the seller; it is a state guard, not a transport failure.","triggerScenarios":"PUT/PATCH to the commissions update endpoint (the before_action at app/controllers/commissions_controller.rb:7 fires for every file change) after (a) the commission was completed, or (b) a completion purchase exists even though status is still in_progress (completion charge settling in the buyer's currency). Any file_signed_ids update in those states raises immediately.","commonSituations":"Seller keeps a stale commission edit page open, completes the commission in another tab, then saves file changes from the stale tab. Or the UI allows re-uploading files while a completion charge is in flight; the serialized files_are_editable? flag exists precisely so the UI affordances match what the controller accepts.","solutions":["Gate the edit affordance on the commission's files_are_editable? value (already serialized to the seller UI) instead of status alone, so file controls disappear the moment a completion purchase exists.","If the completion charge is merely settling (commission still in_progress but completion_purchase present), disable file edits with a 'completion in progress' note and re-check after the charge settles.","If the commission is genuinely completed, stop attempting file mutations — the state is terminal by design; deliverables were justified by the charge.","In API clients, rescue ActiveRecord::RecordInvalid from this endpoint and surface commission.errors.full_messages to the user."],"exampleFix":"# before (controller-side callers / client code)\ncommission.update!(permitted_params) # raises RecordInvalid after completion\n\n# after\ndef update_files(commission, file_signed_ids)\n  return { locked: true, message: \"Files can no longer be changed.\" } unless commission.files_are_editable?\n\n  commission.update!(file_signed_ids:)\nend","handlingStrategy":"validation","validationCode":"# Before PUT/PATCH of commission files\nreturn render_locked(commission) unless commission.files_are_editable?","typeGuard":null,"tryCatchPattern":"begin\n  commission.update!(permitted_params)\nrescue ActiveRecord::RecordInvalid\n  render json: { errors: commission.errors.full_messages }, status: :unprocessable_entity\nend","preventionTips":["Drive file-edit affordances from the serialized files_are_editable? flag, not raw status — it already accounts for a settling completion purchase.","Treat a completion purchase's existence (even while in_progress) as a file lock.","Reload the commission before showing edit UI in long-lived sessions."],"tags":["commissions","rails","activerecord","recordinvalid","state-guard","file-upload"],"backgroundTag":"activerecord-record-invalid","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}