The WithinThreadExecutor calls the execute in the calling thread, the exception would propagate up the stack as is.

This is all it does:
   @Override
   public void execute(Runnable command) {
      command.run();
   }

If not then something sounds very fishy.  I agree a get would be required for submit, but calling execute it should work.  And the synchronous nature is already handled since the call would be done on the same thread.

Testing this quickly in AbstractCacheStoreTest it propagated the exception properly

   @Test(expectedExceptions = RuntimeException.class)
   void testPurgeExpiredThrowsExceptionSync() throws CacheLoaderException {
      cfg.setPurgeSynchronously(true);
      cs.start();
      Mockito.doThrow(new RuntimeException()).when(cs).purgeInternal();
      cs.purgeExpired();
   }