There may be a cleaner way to do this, but I'd got with mapping the error code to a controller and handling the logic there:
class UrlMappings {
   static mappings = {
      "/$controller/$action?/$id?" { constraints {} }
      "/"(view:"/index")
      "403"(controller: "errors", action: "accessDenied")
      "404"(controller: "errors", action: "notFound")
      "405"(controller: "errors", action: "notAllowed")
      "500"(view: '/error')
   }
}
and then create the corresponding controller (grails-app/conf/controllers/ErrorsController.groovy):
import grails.util.Environment
class ErrorsController extends AbstractController {
   def accessDenied = {}
   def notFound = {}
   def notAllowed = {}
   def serverError = {
      if (Environment.current == Environment.DEVELOPMENT) {
         render view: '/error'
      }
      else {
         render view: '/errorProd'
      }
   }
}
You'll need to create the corresponding GSPs in grails-app/views/errors (accessDenied.gsp, notFound.gsp, etc.) and also the new grails-app/views/errorProd.gsp. By routing to a controller method for all error codes you make it easier to add logic to the other error code handlers in the future.