I have added page templates via plugin. It works absolutely fine with classic editor but while using Gutenberg, the page can not be published when page template is assigned. However, page templates added via theme works fine and page is saved as well. But page templates added via plugins creates issues as page cannot be published with those page templates (added via plugin) assigned.

This is how I have added page templates via plugin:

 * Destination template.

function wpte_get_destination_template( $template ) {
    $post = get_post();
    $page_template = get_post_meta( $post->ID, '_wp_page_template', true );
    if( $page_template == 'templates/template-destination.php' ){
        $template_path = locate_template( array( 'template-destination.php' ) );
            $template_path = BASE_PATH . '/includes/templates/template-destination.php';
        return $template_path;
    return $template;

 * Destination template returned.
function wpte_filter_admin_page_templates( $templates ) {
    $templates['templates/template-destination.php'] = __( 'Destination Template','' );
    return $templates;

 * Destination template added.
add_action( 'wp_loaded', array( $this, 'wpte_add_destination_templates' ) );
function wpte_add_destination_templates() {
    if( is_admin() ) {
        add_filter( 'theme_page_templates', array( $this, 'wpte_filter_admin_page_templates' ) );
    else {
        add_filter( 'page_template', array( $this, 'wpte_get_destination_template' ) );

This is the error seen on console when 'Publish' button is pressed after selecting plugin's page templates:

    {"code":"rest_invalid_param","message":"Invalid parameter(s): template","data":{"status":400,"params":{"template":"template is not one of templates\/about.php, templates\/contact.php, templates\/team.php, templates\/testimonial.php."}}}

All of these templates are of theme. Page templates added via plugin is not listed there. Though, they are available in the templates drop-down.

Any help would be more than appreciable.

  • check if your plugin (which you use to create page templates), compatible with Gutenberg – Vishwa Apr 11 '19 at 5:39
  • That is my own plugin. I mean I am the developer. Works fine classical editor though. :( – saurav.rox Apr 11 '19 at 6:57
  • that's what I mean. if it's working fine when you use classical editor, then it must be either problem with gutenberg conflict with your plugin. try using your plugin in fresh wp installation – Vishwa Apr 11 '19 at 8:13
  • add_action( 'wp_loaded', $plugin_admin, 'wpte_add_destination_templates' ); is not valid. What is $plugin_admin supposed to be? – Jacob Peattie Apr 11 '19 at 8:43
  • That is object of a different class. The plugin is developed in OOP approach. – saurav.rox Apr 11 '19 at 8:46

(Revised answer)

Gutenberg (or the Block Editor) uses the WordPress REST API when working with a Page/post (creating, updating, etc.) and the REST API will check whether the template is valid for the given post through WP_REST_Posts_Controller::check_template() and when the template is not valid, the error template is not one of will be thrown.

And as I (recently) stated in this answer:

By default, is_admin() returns false on a REST API endpoint/URL. So if for example you're on http://example.com/wp-json/wp/v2/posts (or that you make API request to that endpoint), then:

if ( is_admin() ) {
    // code here does **not** run

So that should answer the question, where in your wpte_add_destination_templates function, the is_admin() check fails and the custom template is not added to the list of valid/registered templates; and eventually results in the error template is not one of.

Possible Solutions

Hook to both wp_loaded and rest_api_init

add_action( 'wp_loaded', array( $this, 'wpte_add_destination_templates' ) );     // for admin requests
add_action( 'rest_api_init', array( $this, 'wpte_add_destination_templates' ) ); // for REST requests

And in the wpte_add_destination_templates function:

// If REST_REQUEST is defined (by WordPress) and is a TRUE, then it's a REST API request.
$is_rest_route = ( defined( 'REST_REQUEST' ) && REST_REQUEST );
if (
    ( is_admin() && ! $is_rest_route ) || // admin and AJAX (via admin-ajax.php) requests
    ( ! is_admin() && $is_rest_route )    // REST requests only
) {
    add_filter( 'theme_page_templates', array( $this, 'wpte_filter_admin_page_templates' ) );

Or hook directly to theme_page_templates

//add_action( 'wp_loaded', array( $this, 'wpte_add_destination_templates' ) ); // remove
add_filter( 'theme_page_templates', array( $this, 'wpte_filter_admin_page_templates' ) );

And then your wpte_filter_admin_page_templates would be:

function wpte_filter_admin_page_templates( $templates ) {
    // If it's an admin or a REST API request, then filter the templates.
    if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
        $templates['templates/template-destination.php'] = __( 'Destination Template','' );
    } // else, do nothing (i.e. don't modify $templates)

    return $templates;
  • I used your first option and it worked. So, marking it as resolved. Thanks a lot for the help! – saurav.rox Apr 19 '19 at 5:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.