Ah! That makes total sense. I was starting down that line of thinking by entering my input as 'right'::side in the console, but still didn't figure it out. Of course it has to be cast after input. The world makes sense again. Thanks!
I've trimmed my example down to the bare minimum in hopes of solving the problem myself, but no luck. Here's the exact code and console session:
create type side as enum ('right', 'left');
create or replace function testinput(
p_units_alignside
) returns void as
....
contracts=# select * from testinput('blue');
ERROR: 22P02: invalid input value for enum side: "blue"
LINE 1: select * from testinput('blue');
^
LOCATION: enum_in, enum.c:57
Well, the point here is that the system has to convert 'blue' to a value of type "side" before it ever invokes your function. So there's no hope of trapping that failure inside the function.
If you really want to do things this way, you can declare the function as taking a text string, and cast from text to "side" within the function's exception-trapping block.