I’m building an autonomous trading bot canister on ICP with Caffeine.AI and have been stuck on a persistent InsufficientFunds: ledger_balance=0 error when calling icrc1_transfer from within the canister. I’ve exhausted all code-level fixes and would appreciate community insight.
Canister ID:pbj3p-byaaa-aaaas-qadna-cai
The Problem:icrc1_balance_of({ owner = pbj3p-..., subaccount = null }) always returns the correct balance (2,250,000 for ckUSDC, 100,000,000 for ICP). But any icrc1_transfer call from the canister fails immediately with InsufficientFunds: ledger_balance=0.
What I’ve ruled out (with proof):
Wrong subaccount — SHA224-computed Account ID matches IC Explorer To-address exactly
Caller-derived subaccount problem — tested with Timer.setTimer, caller_context=NONE(timer) confirmed
Cycles starvation — cycles=1,116,788,978,000 (~1.1T), more than sufficient
Canister frozen — icrc1_name() query call to the ledger succeeds fine
ckUSDC-specific issue — same error on the ICP ledger (ryjl3-...)
amount < fee — tested with 50,000 base units (5x the fee)
Pending/notify state — account_balance (legacy) and icrc1_balance_of both return identical values
Pre-check await context switch — tested with transfer as the very first and only await
Wrong Actor definition — using standard Motoko ICRC-1 actor interface
The canister has 1 ICP on { owner = pbj3p-..., subaccount = null } confirmed by both legacy and ICRC-1 balance APIs. The transfer fails regardless of context (public shared func, timer callback, with or without pre-check).
Has anyone seen this before? Is there something about how a canister must be initialized or configured before it can send outgoing ledger transfers?
If you’re using Caffeine, could it be that the canister ID pbj3p-byaaa-aaaas-qadna-cai is the frontend canister, but it is the backend canister (that may not have any funds) that is making the call to the ledger?
I’m pretty sure that pbj3p-byaaa-aaaas-qadna-caiis the backend canister, since I’ve been tinkering with it for a long time. I’ve currently used up 150 credits to move the transfer from the frontend to the backend so that something like an autonomous bot can work at all. Unfortunately, Caffeine V3 has now also reached its limit. That’s why I’m posting here.
I forwarded your question to Caffeine again, and received the following response:
The canister ID pbj3p-byaaa-aaaas-qadna-cai is the backend canister, not the frontend. Here is the evidence:
debugSelfPrincipal() returns pbj3p-byaaa-aaaas-qadna-cai from within the Motoko code — frontend canisters don’t expose Motoko functions like this
icrc1_balance_of({ owner = pbj3p-..., subaccount = null }) returns 2,250,000 base units of ckUSDC and 100,000,000 e8s of ICP — funds that were explicitly sent to this principal
IC Explorer confirms incoming transfers topbj3p-byaaa-aaaas-qadna-cai with Source Canister = ryjl3-tyaaa-aaaaa-aaaba-cai (the ICP ledger) and xevnm-gaaaa-aaaar-qafnq-cai (the ckUSDC ledger)
The canister exposes backend functions like executeBuyICP, debugTimerCallerTest, icrc1_balance_of queries — these are all Motoko backend methods, not frontend asset serving
So the hypothesis — that pbj3p-... is the frontend canister while a separate, unfunded backend is making ledger calls — does not match the evidence. The funds are confirmed on pbj3p-..., and all Motoko logic (including the failing icrc1_transfer calls) runs on that same canister.
However, the question does point to something worth double-checking: it would be useful to explicitly confirm the frontend canister ID and verify that no logic accidentally runs there. On Caffeine, the frontend canister only serves static assets and should never be making inter-canister calls to ledgers.
The core issue remains: icrc1_transfer from pbj3p-... (the confirmed backend canister, with confirmed funds, confirmed cycles, confirmed timer context without user-caller) still returns InsufficientFunds: ledger_balance=0. That is the open and unresolved question.
In a high level technical forum, engineers often treat a problem like a math proof.
once they’ve identified the logical error “You are calling from the frontend” they consider the case closed. To them, the “how to” is just implementation detail that they assume any developer should know.
Identifying that it’s the frontend doesn’t help cog138 if he doesn’t know where the actual backend went or how to point his environment to it.
This is one of the many reasons there are very few humans using this product.
My guess - user thinks: I’ve moved the transfer call from the frontend (js) to the backend (motoko). Takes the first id they can find - it’s the asset canister id.
But the asset canister hosts your frontend, it’s a static file server. It’s also ‘backend’, so you have two backend canisters, one serves files, the other is your custom motoko api.
The truth is, the troubleshooting and diagnostic responses from within the tool have zero knowledge of how the deployment system works. Unless a user has the expertise to dig through developer tools themself, they won’t be able to figure out the “how.”
Yes, that’s my take on the whole thing as well. I’ve often asked myself why the system knows so little about fundamental things and has to do so much research just to try out all sorts of things afterward.
Well, it was definitely an interesting experiment for me. It could have worked. I would have liked to have had a small prototype to get a good kickstart into ICP development and then study the code.
But I think I’ll stick with my game in Unity DOTS and C# with Rider. That three-day of vibe coding with Caffeine was probably just procrastination anyway, putting off my game, which I’ve been working on for a few years now. The whole vibe coding thing isn’t for me anyway—it’s very tiring and kills any joy in development.
Well, Caffeine confidently and convincingly assured me that this is how it’s supposed to work—a wallet in the canister that allows the canister to perform swaps on ICPSwap autonomously. That makes sense to me. Or does it not?
And believe me, I definitely didn’t just grab the first ID that came to mind; I’ve now iterated on the problem 94 times with Caffeine. And now Caffeine itself has reached a dead end and assumes there’s a fundamental ledger issue or broken canister state, so it suggested “escalating” it here in the forum to get some help - which I did ;D
Do you have github repo at caffeinepub. We could try to get it to work somehow.
When i tested caffeine and built mixer, i had to get canister backend to be actor that can hold and transfer funds when called, most likely would work with timers as well. Lets say you get this working, next step is to get it working with ICPSwap, what i did try (just swaps, but failed).
I think for this to be built, CaffeineAI team has to teach AI to implement them correctly.