I have a post type the uses post_save to take the address from the post-meta and retrieve the lat/lng coordinates from the Google API. I need a way of notifying the user if there was an issue with retrieving the coordintes. I tried using admin_notices, but nothing displayed:

public static function update_notice() {
  echo "<div class='error'><p>Failed to retrieve coordinates. Please check key and address.<p></div>";
  remove_action('admin_notices', 'update_notice');

add_action('admin_notices', array('GeoPost', 'update_notice'));

I'm not sure if I'm using it incorrectly or in the wrong context. To be clear, in the actual code the add_action is in another function in the same class. That's working fine.


The reason this doesn't work is because there is a redirection happening after the save_post action. One way you can acheive want you want is by implementing a quick work around using query vars.

Here is a sample class to demonstrate:

class My_Awesome_Plugin {
  public function __construct(){
   add_action( 'save_post', array( $this, 'save_post' ) );
   add_action( 'admin_notices', array( $this, 'admin_notices' ) );

  public function save_post( $post_id, $post, $update ) {
   // Do you stuff here
   // ...

   // Add your query var if the coordinates are not retreive correctly.
   add_filter( 'redirect_post_location', array( $this, 'add_notice_query_var' ), 99 );

  public function add_notice_query_var( $location ) {
   remove_filter( 'redirect_post_location', array( $this, 'add_notice_query_var' ), 99 );
   return add_query_arg( array( 'YOUR_QUERY_VAR' => 'ID' ), $location );

  public function admin_notices() {
   if ( ! isset( $_GET['YOUR_QUERY_VAR'] ) ) {
   <div class="updated">
      <p><?php esc_html_e( 'YOUR MESSAGE', 'text-domain' ); ?></p>

Hope this helps you a little bit. Cheers

| improve this answer | |
  • Works great, thanks! But there's a missing closing bracket in the first line in the public function admin_notices() (an extra closing bracket in the if ( ! isset(.. line) – Rhys Wynne Jan 28 '15 at 16:04
  • I've added remove_query_arg('YOUR_QUERY_VAR'); as i found it can be set from the last update. – Tony O'Hagan Jul 6 '18 at 8:12
  • +1 Good answer. – Mark Aug 7 '18 at 7:35

Made a wrapper class for this kind of scenario. Actually the class can be used in any scenario involving displaying notices. I use the PSR standards, so the naming is atypical of Wordpress code.

class AdminNotice
    const NOTICE_FIELD = 'my_admin_notice_message';

    public function displayAdminNotice()
        $option      = get_option(self::NOTICE_FIELD);
        $message     = isset($option['message']) ? $option['message'] : false;
        $noticeLevel = ! empty($option['notice-level']) ? $option['notice-level'] : 'notice-error';

        if ($message) {
            echo "<div class='notice {$noticeLevel} is-dismissible'><p>{$message}</p></div>";

    public static function displayError($message)
        self::updateOption($message, 'notice-error');

    public static function displayWarning($message)
        self::updateOption($message, 'notice-warning');

    public static function displayInfo($message)
        self::updateOption($message, 'notice-info');

    public static function displaySuccess($message)
        self::updateOption($message, 'notice-success');

    protected static function updateOption($message, $noticeLevel) {
        update_option(self::NOTICE_FIELD, [
            'message' => $message,
            'notice-level' => $noticeLevel


add_action('admin_notices', [new AdminNotice(), 'displayAdminNotice']);
AdminNotice::displayError(__('An error occurred, check logs.'));

The notice is displayed once.

| improve this answer | |

In addition to @jonathanbardo's answer which is great and functions well, if you want to remove the query argument after the new page is loaded, you can use the removable_query_args filter. You get an array of argument names to which you can append your own argument. Then WP will take care of removing all of the arguments in the list from the URL.

public function __construct() {
    add_filter('removable_query_args', array($this, 'add_removable_arg'));

public function add_removable_arg($args) {
    array_push($args, 'my-query-arg');
    return $args;

Something like:


Will become:

| improve this answer | |

Simple, elegant, based on get_settings_errors().

function wpse152033_set_admin_notice($id, $message, $status = 'success') {
    set_transient('wpse152033' . '_' . $id, [
        'message' => $message,
        'status' => $status
    ], 30);

function wpse152033_get_admin_notice($id) {
    $transient = get_transient( 'wpse152033' . '_' . $id );
    if ( isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] && $transient ) {
        delete_transient( 'wpse152033' . '_' . $id );
    return $transient;


In your post request handler:

wpse152033_set_admin_notice(get_current_user_id(), 'Hello world', 'error');
wp_redirect(add_query_arg('settings-updated', 'true',  wp_get_referer()));

Where you want to use the admin notice, usually in the admin_notices hook.

$notice = $this->get_admin_notice(get_current_user_id());
if (!empty($notice) && is_array($notice)) {
    $status = array_key_exists('status', $notice) ? $notice['status'] : 'success';
    $message = array_key_exists('message', $notice) ? $notice['message'] : '';
    print '<div class="notice notice-'.$status.' is-dismissible">'.$message.'</div>';
| improve this answer | |

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.