Something that jumps out at me but doesn't explain your problem is that $! is for system errors not DBI errors. Those would be in $DBI::errstr, or you could just set RaiseError in the connect. But like I said, that doesn't explain this behavior unless there's some other loop around this loop that you're not showing.