{"record":{"id":"bc640ca6abd59160","repo":"antiwork/gumroad","slug":"please-select-a-start-time","errorCode":null,"errorMessage":"Please select a start time.","messagePattern":"Please select a start time\\.","errorType":"exception","errorClass":"Purchase::PurchaseInvalid","httpStatus":null,"severity":"error","filePath":"app/services/purchase/create_service.rb","lineNumber":467,"sourceCode":"          if variant.present?\n            purchase.variant_attributes << variant\n          else\n            purchase.errors.add(:base, \"The product's variants have changed, please refresh the page!\")\n            raise Purchase::PurchaseInvalid, \"The product's variants have changed, please refresh the page!\"\n          end\n        end\n      elsif product.is_tiered_membership\n        purchase.variant_attributes << product.tiers.first\n      elsif product.is_physical && product.skus.is_default_sku.present?\n        purchase.variant_attributes << product.skus.is_default_sku.first\n      end\n\n      if product.native_type == Link::NATIVE_TYPE_CALL\n        start_time = Time.zone.parse(params[:call_start_time] || \"\")\n        duration_in_minutes = purchase.variant_attributes.first&.duration_in_minutes\n\n        if start_time.blank? || duration_in_minutes.blank?\n          raise Purchase::PurchaseInvalid, \"Please select a start time.\"\n        end\n\n        end_time = start_time + duration_in_minutes.minutes\n        purchase.build_call(start_time:, end_time:)\n      end\n\n      build_custom_fields(purchase, params[:custom_fields] || [], product:)\n\n      product.bundle_products.alive.each do |bundle_product|\n        # Temporarily create custom fields on the bundle purchase in case it can't complete yet due to SCA.\n        # The custom fields will be moved to each product purchase when the receipt is generated.\n        custom_fields_params = params[:bundle_products]&.find { _1[:product_id] == bundle_product.product.external_id }&.dig(:custom_fields)\n        build_custom_fields(purchase, custom_fields_params || [], bundle_product:)\n      end\n\n      purchase.url_parameters = parse_url_parameters(params_for_purchase[:url_parameters])\n      purchase\n    end","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/services/purchase/create_service.rb#L449-L485","documentation":"For call-type products (native_type == Link::NATIVE_TYPE_CALL), checkout requires a schedulable slot: Time.zone.parse(params[:call_start_time]) must yield a time and the first variant attribute must define duration_in_minutes so end_time can be computed. A missing, blank, or unparseable start time - or a variant without a duration - raises 'Please select a start time.'","triggerScenarios":"product.native_type == Link::NATIVE_TYPE_CALL with params[:call_start_time] missing, empty, or in a format Time.zone.parse rejects (returns nil), OR purchase.variant_attributes.first.duration_in_minutes blank.","commonSituations":"Buyer never picked a slot and the client submitted anyway; datetime string without a parseable format or zone; call product whose selected variant/tier lacks duration configuration.","solutions":["Select a start time and resubmit using an ISO 8601 string with zone, e.g. 2026-08-21T15:00:00-07:00.","Ensure the selected call variant defines duration_in_minutes.","Integrators: require a chosen slot client-side and validate the string parses before submit."],"exampleFix":"# before: empty/unparseable start time\nparams[:call_start_time] = ''\n\n# after: send an ISO 8601 timestamp with an explicit offset\nparams[:call_start_time] = '2026-08-21T15:00:00-07:00'","handlingStrategy":"validation","validationCode":"if product.native_type == Link::NATIVE_TYPE_CALL\n  raise ArgumentError, 'call_start_time required' if Time.zone.parse(params[:call_start_time].to_s).blank?\nend","typeGuard":"def parseable_start_time?(value)\n  Time.zone.parse(value.to_s).present?\nend","tryCatchPattern":"begin\n  Purchase::CreateService.new(product:, params:, buyer:).perform\nrescue Purchase::PurchaseInvalid => e\n  # require the buyer to pick a slot; no automated recovery\n  render_checkout_error(e.message) if e.message == 'Please select a start time.'\nend","preventionTips":["Require a selected slot client-side before enabling submit.","Send start times as ISO 8601 strings with an explicit zone offset.","Verify the selected call variant defines duration_in_minutes before checkout."],"tags":["calls","scheduling","datetime","checkout","gumroad"],"backgroundTag":"missing-scheduled-time","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}