printf takes an item list after the format string, so everything is in list context. To force into scalar context you can use
scalar or a scalar operator, such as . (dot). The reason your assignment works is because the left side is a scalar, which also forces the right to be scalar context.
You don't really need printf here:
print 'keys: '.keys(%hash)."\n";
uses the . operator to force into scalar.