why does chvt work fine as a regular user on a virtual terminal, but not under X?

Solution 1:

From a security point-of-view, recall that X is designed to be used over the network as well as at the physical console. Virtual terminals aren't, so a login at the physical VT is a different security beast from X.

From this Debian bug report on the same error in fgconsole. Here's the breakdown of why chvt and friends can't access it from X:

It doesn’t only affect fgconsole, but also chvt, openvt and any other kbd utility which tries to get a console file descriptor. These programs do their job by trying to open/ioctl these files (in this order):

/proc/self/fd/0         (is a pseudo tty in your case)
/dev/tty                (also PTY)
/dev/tty0               (only accessible to root)
/dev/vc/0               (doesn’t exist nowadays)
/dev/console            (root)
std{in,out,err}         (PTY)

As none of these is able to respond to a VT_GETSTATE ioctl, fgconsole and friends fail.

The preferred solution to the issue is to use sudo. There are other options but they are less preferable in the general case due to security issues -- you may decide your situation can tolerate one or the other (emphasis mine):

So there is no real solution, if I don't use sudo?

You cannot change the permissions of /dev/tty0 or /dev/console without opening a giant security hole. Setting the desired programs suid is an option, but I’d stay away from it. sudo is much safer and can be controlled in more detail.