diff --git a/node_embed.module b/node_embed.module
index 99e0be2..1745040 100644
--- a/node_embed.module
+++ b/node_embed.module
@@ -46,36 +46,103 @@ function node_embed_filter_node_embed_tips($filter, $format, $long) {
 /**
  * Provides the replacement html to be rendered in place of the embed code.
  *
- * Does not handle nested embeds.
- *
  * @param array $matches
- *   numeric node id that has been captured by preg_replace_callback.
+ *   Embedded code, numeric node id and parameters that has been captured
+ *   by preg_replace_callback.
  *
  * @return string
- *   The rendered HTML replacing the embed code.
+ *   The rendered HTML replacing the embed code. If embedding fails an empty
+ *   string will be returned for users without update access or else the
+ *   embed code.
  */
 function _node_embed_replacements($matches) {
-  $node = node_load($matches[1]);
+  static $stack;
 
-  if ($node == FALSE || !node_access('view', $node) || !$node->status) {
-    return "[[nid:{$matches[1]}]]";
+  // Add the node page to the stack.
+  if (empty($stack) && $node_page = menu_get_object('node')) {
+    $stack[$node_page->nid] = $node_page;
   }
-  else {
-    $node->node_embed_parameters = array();
 
-    if (isset($matches[2]) && trim($matches[2]) != '') {
-      parse_str(trim(str_replace('&nbsp;', ' ', $matches[2])), $node->node_embed_parameters);
-    }
+  // Check if node isn't already embedded.
+  if (array_key_exists($matches[1], $stack)) {
+    return _node_embed_user_feedback(end($stack), $matches, 'loop');
+  }
 
-    if (!isset($node->node_embed_parameters['view_mode'])) {
-      $node->node_embed_parameters['view_mode'] = 'node_embed';
-    }
+  // Check if the node is a real node.
+  if (!$node = node_load($matches[1])) {
+    return _node_embed_user_feedback(end($stack), $matches, 'invalid');
+  }
+
+  // Add to stack.
+  $stack[$node->nid] = $node;
+
+  // Check if node can be viewed by the user.
+  if (!node_access('view', $node)) {
+    return _node_embed_user_feedback(end($stack), $matches, 'access');
+  }
+
+  // Warn about an unpublished node.
+  if ($node->status == NODE_NOT_PUBLISHED) {
+    _node_embed_user_feedback($node, $matches, 'unpublished');
+  }
 
-    $view = node_view($node, $node->node_embed_parameters['view_mode'], NULL);
-    $render = drupal_render($view);
+  $node->node_embed_parameters = array();
+  if (isset($matches[2]) && trim($matches[2]) != '') {
+    parse_str(trim(str_replace('&nbsp;', ' ', $matches[2])), $node->node_embed_parameters);
+  }
 
-    return $render;
+  if (!isset($node->node_embed_parameters['view_mode'])) {
+    $node->node_embed_parameters['view_mode'] = 'node_embed';
   }
+
+  $view = node_view($node, $node->node_embed_parameters['view_mode']);
+  $render = drupal_render($view);
+
+  // Node without any problems, remove it from stack.
+  unset($stack[$node->nid]);
+
+  return $render;
+}
+
+/**
+ * Helper function for returning feedback to the user.
+ */
+function _node_embed_user_feedback(stdClass $parent_node, $matches, $type) {
+  // There is no point to show the embed code if the user can't edit the node.
+  if (!node_access('update', $parent_node)) {
+    return '';
+  }
+
+  $parent_node_url = l($parent_node->title, 'node/' . $parent_node->nid);
+
+  switch ($type) {
+    case 'access' :
+      drupal_set_message(t('You don\'t have the permission to view embedded node ID @nid in !node.', array(
+        '@nid' => $matches[1],
+        '!node' => $parent_node_url,
+      )));
+      break;
+    case 'unpublished' :
+      drupal_set_message(t('The embedded node !node is unpublished.', array(
+        '@nid' => $matches[1],
+        '!node' => $parent_node_url,
+      )));
+      break;
+    case 'loop' :
+      drupal_set_message(t('Loop detected with embedding code @code in !node', array(
+        '@code' => $matches[0],
+        '!node' => $parent_node_url,
+      )));
+      break;
+    case 'invalid' :
+      drupal_set_message(t('The embedded node ID @nid in !node does not exist (anymore).', array(
+        '@nid' => $matches[1],
+        '!node' => $parent_node_url,
+      )));
+      break;
+  }
+
+  return $matches[0];
 }
 
 /**
@@ -162,40 +229,6 @@ function node_embed_views_data_alter(&$data) {
 }
 
 /**
- * Implements hook_form_alter().
- *
- * Add a validation handler to nodes with node_embed.
- */
-function node_embed_form_alter(&$form, &$form_state, $form_id) {
-  $form['#validate'][] = 'node_embed_validate';
-}
-
-/**
- * Validation for the node_embed filter.
- *
- * We do not allow nodes to embed in themselves.
- * results in segment fault.
- */
-function node_embed_validate($node, $form) {
-  if (isset($form['values']['nid'])) {
-    $nid = $form['values']['nid'];
-    $needle = "[[nid:{$nid}]]";
-    $num = 0;
-    $language = (isset($form['values']['language']) && $form['values']['language']) ? $form['values']['language'] : LANGUAGE_NONE;
-
-    while (isset($form['values']['body'][$language][$num])) {
-      $found = strpos($form['values']['body'][$language][$num]['value'], $needle);
-
-      if ($found !== FALSE) {
-        form_set_error('edit-body', t('A node is not allowed to embed in itself.'));
-      }
-
-      $num++;
-    }
-  }
-}
-
-/**
  * Implementing the Wysiwyg API.
  *
  * Register a directory containing Wysiwyg plugins.
