{"record":{"id":"4533c4ae6230b065","repo":"medusajs/medusa","slug":"invalid-data-4533c4","errorCode":"invalid_data","errorMessage":"Payment collection has not been initiated for cart","messagePattern":"Payment collection has not been initiated for cart","errorType":"validation","errorClass":"MedusaError","httpStatus":400,"severity":"error","filePath":"packages/core/core-flows/src/cart/steps/validate-cart-payments.ts","lineNumber":53,"sourceCode":" *   cart\n * })\n */\nexport const validateCartPaymentsStep = createStep(\n  validateCartPaymentsStepId,\n  async (data: ValidateCartPaymentsStepInput) => {\n    const {\n      cart: { payment_collection: paymentCollection, total, credit_line_total },\n    } = data\n\n    const canSkipPayment =\n      MathBN.convert(credit_line_total).gte(0) && MathBN.convert(total).lte(0)\n\n    if (canSkipPayment) {\n      return new StepResponse([])\n    }\n\n    if (!isPresent(paymentCollection)) {\n      throw new MedusaError(\n        MedusaError.Types.INVALID_DATA,\n        `Payment collection has not been initiated for cart`\n      )\n    }\n\n    // We check if any of these payment sessions are present in the cart\n    // If not, we throw an error for the consumer to provide a processable payment session\n    const processablePaymentStatuses = [\n      PaymentSessionStatus.PENDING,\n      PaymentSessionStatus.REQUIRES_MORE,\n      PaymentSessionStatus.AUTHORIZED, // E.g. payment was authorized, but the cart was not completed\n      PaymentSessionStatus.CAPTURED, // E.g. payment was captured, but the cart was not completed\n      PaymentSessionStatus.PENDING_AUTHORIZATION, // E.g. async payment method, authorization is deferred\n    ]\n\n    const paymentsToProcess = paymentCollection.payment_sessions?.filter((ps) =>\n      processablePaymentStatuses.includes(ps.status as PaymentSessionStatus)\n    )","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/core/core-flows/src/cart/steps/validate-cart-payments.ts#L35-L71","documentation":"updateStoreCreditAccounts in the loyalty store-credit service validates each account payload against a whitelist of updatable fields. As written, the check `if (whitelistedFields.includes(key)) throw` fires when a key IS in the whitelist — the condition appears inverted versus the message's intent, so supplying `id` or `metadata` throws 'Field ... is not allowed to be updated', and non-whitelisted fields pass through.","triggerScenarios":"Calling updateStoreCreditAccounts with any object that includes an `id` or `metadata` key — which is practically every update payload, since `id` is normally required to target the record.","commonSituations":"Any caller following the standard Medusa update pattern { id, ...changes } hits this immediately; it effectively makes the method unusable until fixed.","solutions":["Fix the guard in the plugin source: throw only when !whitelistedFields.includes(key), and require `id` rather than blacklist it","If you cannot patch the plugin, avoid this method and update via a custom service/query until fixed","Report/patch upstream in packages/plugins/loyalty"],"exampleFix":"// before\nconst whitelistedFields = [\"id\", \"metadata\"]\nObject.keys(account).forEach((key) => { if (whitelistedFields.includes(key)) throw new Error(`Field ${key} is not allowed to be updated`) })\n// after\nconst whitelistedFields = [\"id\", \"metadata\"]\nfor (const key of Object.keys(account)) { if (key !== \"id\" && !whitelistedFields.includes(key)) throw new Error(`Field ${key} is not allowed to be updated`) }","handlingStrategy":"validation","validationCode":"const allowed = new Set(['metadata'])\nconst bad = Object.keys(payload).filter(k => k !== 'id' && !allowed.has(k))\nif (bad.length) throw new Error(`Fields not allowed: ${bad.join(', ')}`)\n// only call the (patched) service when payload is clean","typeGuard":"const isCleanUpdate = (o: Record<string, unknown>) => Object.keys(o).every(k => k === 'id' || k === 'metadata')","tryCatchPattern":"catch (e) { if (e.message.includes('not allowed to be updated')) return badRequest(e.message); throw e }","preventionTips":["Patch the inverted whitelist check in the plugin before using this method","Send only { id, metadata } payloads until the guard is fixed"],"tags":["loyalty-plugin","store-credit","validation","plugin-bug"],"backgroundTag":"field-whitelist-validation","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}