{"record":{"id":"e93895a5b8911f9a","repo":"HeyPuter/puter","slug":"insufficient-funds-e93895","errorCode":"insufficient_funds","errorMessage":"Insufficient credits for image generation","messagePattern":"Insufficient credits for image generation","errorType":"http","errorClass":"HttpError","httpStatus":402,"severity":"error","filePath":"src/backend/drivers/ai-image/providers/cloudflare/CloudflareImageProvider.ts","lineNumber":134,"sourceCode":"                : singleInput;\n        }\n\n        const steps = this.#resolveSteps(selectedModel, options);\n        const costComponents = this.#estimateCost(selectedModel, ratio, steps, {\n            hasInputImage:\n                typeof options.image === 'string' &&\n                options.image.trim() !== '',\n        });\n        const totalCostInMicroCents = costComponents.reduce(\n            (acc, component) => acc + component.totalCostMicroCents,\n            0,\n        );\n        const usageAllowed = await this.#meteringService.hasEnoughCredits(\n            actor,\n            totalCostInMicroCents,\n        );\n        if (!usageAllowed) {\n            throw new HttpError(\n                402,\n                'Insufficient credits for image generation',\n                { legacyCode: 'insufficient_funds' },\n            );\n        }\n\n        const response = await this.#runModel(selectedModel, {\n            ...options,\n            ratio,\n            steps,\n        });\n\n        this.#meteringService.batchIncrementUsages(\n            actor,\n            costComponents\n                .filter(\n                    (component) =>\n                        component.usageAmount > 0 &&","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/drivers/ai-image/providers/cloudflare/CloudflareImageProvider.ts#L116-L152","documentation":"CloudflareImageProvider computes the generation cost from cost components and asks meteringService.hasEnoughCredits(actor, totalCostInMicroCents). If the user can't cover the cost, it throws HTTP 402 insufficient_funds before calling Cloudflare — no credits are spent. This is a pre-flight credit check.","triggerScenarios":"A user whose credit balance is below the computed cost of the requested Cloudflare image generation attempts to generate.","commonSituations":"Free-tier users out of credits; high-cost model/params that exceed remaining balance; billing/quota not topped up.","solutions":["Top up the user's credits / upgrade plan.","Lower cost drivers (fewer steps, cheaper model) to fit within balance.","Catch HTTP 402 insufficient_funds and prompt the user to add credits."],"exampleFix":"// before\nawait provider.generate({ prompt, model: 'expensive-model', steps: 50 });\n// after — handle insufficient funds\ntry { await provider.generate({ prompt }); }\ncatch (e) {\n  if (e.code === 'insufficient_funds') { promptAddCredits(); return; }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Pre-check balance if you expose metering to the caller.\nconst enough = await meteringService.hasEnoughCredits(actor, estimatedCostMicroCents);\nif (!enough) promptAddCredits();","typeGuard":null,"tryCatchPattern":"try {\n  await provider.generate({ prompt });\n} catch (e) {\n  if (e?.code === 'insufficient_funds') { promptAddCredits(); return; }\n  throw e;\n}","preventionTips":["Top up credits before high-cost generations.","Catch HTTP 402 insufficient_funds and prompt the user to add credits.","Prefer cheaper model/step counts when balance is low."],"tags":["ai-image","cloudflare","billing","credits","insufficient-funds"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}