{"record":{"id":"5ebe3e278038e807","repo":"phacility/phabricator","slug":"unable-to-load-provider-for-charge-5ebe3e","errorCode":null,"errorMessage":"Unable to load provider for charge!","messagePattern":"Unable to load provider for charge!","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/phortune/controller/cart/PhortuneCartUpdateController.php","lineNumber":50,"sourceCode":"    if ($charges) {\n      $providers = id(new PhortunePaymentProviderConfigQuery())\n        ->setViewer($viewer)\n        ->withPHIDs(mpull($charges, 'getProviderPHID'))\n        ->execute();\n      $providers = mpull($providers, null, 'getPHID');\n    } else {\n      $providers = array();\n    }\n\n    foreach ($charges as $charge) {\n      if ($charge->isRefund()) {\n        // Don't update refunds.\n        continue;\n      }\n\n      $provider_config = idx($providers, $charge->getProviderPHID());\n      if (!$provider_config) {\n        throw new Exception(pht('Unable to load provider for charge!'));\n      }\n\n      $provider = $provider_config->buildProvider();\n      $provider->updateCharge($charge);\n    }\n\n    return id(new AphrontRedirectResponse())\n      ->setURI($cart->getDetailURI());\n  }\n\n}\n","sourceCodeStart":32,"sourceCodeEnd":62,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/phortune/controller/cart/PhortuneCartUpdateController.php#L32-L62","documentation":"PhortuneCartUpdateController refreshes each non-refund charge by looking up its providerPHID in the provider-config map and calling updateCharge(). When the charge's provider configuration no longer exists or is not among the loaded providers, idx() yields null and the controller throws 'Unable to load provider for charge!'. The charge originated from a provider that has since been removed or disabled.","triggerScenarios":"Clicking 'Update Charges' (or hitting the update endpoint) on a cart whose provider config was deleted or disabled after checkout; provider restricted away from the merchant; mixed-provider carts where one provider was retired.","commonSituations":"Providers removed after migrating payment processors; test providers disabled in production leaving old carts; provider configs cleaned up during deprovisioning.","solutions":["Re-enable or re-create the missing provider configuration so its charges can be updated and refunded","If retiring a provider, first settle or migrate all charges that reference it","As a last resort, record gateway-side state manually and neutralize the orphaned charges"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Confirm each charge maps to a live provider before updating\nforeach ($charges as $charge) {\n  if (!$charge->isRefund() && idx($providers, $charge->getProviderPHID()) === null) {\n    continue; // or flag the cart for manual reconciliation\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not delete provider configs while charges reference them","Disable providers from new charges instead of uninstalling until balances settle","Run periodic reconciliation of charges against configured providers"],"tags":["phabricator","phortune","payments","provider","charge-update"],"backgroundTag":"missing-provider-configuration","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}