LOOP BEGIN EXECUTE 'bogus command'; EXCEPTION WHEN OTHERS THEN END; END LOOP;
...
Another answer is to invent a third per-function memory context intended to hold statement-lifespan variables.
I've wanted this in the past and been surprised we don't have it, so +1 from me.
I don't think a few MemoryContextAlloc's are too ugly.
I suggest that in cassert builds, or maybe just CACHE_CLOBBER_ALWAYS, the context is reset after each call even when not cleaning up after an error. That'll help catch mistakes and leaks.