diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index fe24e8170876d2c94e323926a69d0dc2936738dc..756414a6c42de83d03a77920474db3f5fc944c97 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -488,6 +488,7 @@ return array(
     'OCP\\Share\\Events\\ShareCreatedEvent' => $baseDir . '/lib/public/Share/Events/ShareCreatedEvent.php',
     'OCP\\Share\\Events\\ShareDeletedEvent' => $baseDir . '/lib/public/Share/Events/ShareDeletedEvent.php',
     'OCP\\Share\\Events\\VerifyMountPointEvent' => $baseDir . '/lib/public/Share/Events/VerifyMountPointEvent.php',
+    'OCP\\Share\\Exceptions\\AlreadySharedException' => $baseDir . '/lib/public/Share/Exceptions/AlreadySharedException.php',
     'OCP\\Share\\Exceptions\\GenericShareException' => $baseDir . '/lib/public/Share/Exceptions/GenericShareException.php',
     'OCP\\Share\\Exceptions\\IllegalIDChangeException' => $baseDir . '/lib/public/Share/Exceptions/IllegalIDChangeException.php',
     'OCP\\Share\\Exceptions\\ShareNotFound' => $baseDir . '/lib/public/Share/Exceptions/ShareNotFound.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 1827ac55d420446b9957006d593a09523fd4fc49..7c7243776f86cb9d1a6b9cdd1fee9a854896d69e 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -517,6 +517,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
         'OCP\\Share\\Events\\ShareCreatedEvent' => __DIR__ . '/../../..' . '/lib/public/Share/Events/ShareCreatedEvent.php',
         'OCP\\Share\\Events\\ShareDeletedEvent' => __DIR__ . '/../../..' . '/lib/public/Share/Events/ShareDeletedEvent.php',
         'OCP\\Share\\Events\\VerifyMountPointEvent' => __DIR__ . '/../../..' . '/lib/public/Share/Events/VerifyMountPointEvent.php',
+        'OCP\\Share\\Exceptions\\AlreadySharedException' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/AlreadySharedException.php',
         'OCP\\Share\\Exceptions\\GenericShareException' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/GenericShareException.php',
         'OCP\\Share\\Exceptions\\IllegalIDChangeException' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/IllegalIDChangeException.php',
         'OCP\\Share\\Exceptions\\ShareNotFound' => __DIR__ . '/../../..' . '/lib/public/Share/Exceptions/ShareNotFound.php',
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index d7e1d0535199fad11c246f885c2d1cc5bd2d8be7..d45a376b71972fec90cd3aae5272df7cef1a722a 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -63,6 +63,7 @@ use OCP\Security\Events\ValidatePasswordPolicyEvent;
 use OCP\Security\IHasher;
 use OCP\Security\ISecureRandom;
 use OCP\Share;
+use OCP\Share\Exceptions\AlreadySharedException;
 use OCP\Share\Exceptions\GenericShareException;
 use OCP\Share\Exceptions\ShareNotFound;
 use OCP\Share\IManager;
@@ -565,7 +566,7 @@ class Manager implements IManager {
 
 			// Identical share already existst
 			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
-				throw new \Exception('Path is already shared with this user');
+				throw new AlreadySharedException('Path is already shared with this user', $existingShare);
 			}
 
 			// The share is already shared with this user via a group share
@@ -575,7 +576,7 @@ class Manager implements IManager {
 					$user = $this->userManager->get($share->getSharedWith());
 
 					if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
-						throw new \Exception('Path is already shared with this user');
+						throw new AlreadySharedException('Path is already shared with this user', $existingShare);
 					}
 				}
 			}
@@ -620,7 +621,7 @@ class Manager implements IManager {
 			}
 
 			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
-				throw new \Exception('Path is already shared with this group');
+				throw new AlreadySharedException('Path is already shared with this group', $existingShare);
 			}
 		}
 	}
@@ -734,77 +735,82 @@ class Manager implements IManager {
 			}
 		}
 
-		//Verify share type
-		if ($share->getShareType() === IShare::TYPE_USER) {
-			$this->userCreateChecks($share);
-
-			//Verify the expiration date
-			$share = $this->validateExpirationDateInternal($share);
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
-			$this->groupCreateChecks($share);
+		try {
+			//Verify share type
+			if ($share->getShareType() === IShare::TYPE_USER) {
+				$this->userCreateChecks($share);
 
-			//Verify the expiration date
-			$share = $this->validateExpirationDateInternal($share);
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
-			$this->linkCreateChecks($share);
-			$this->setLinkParent($share);
+				//Verify the expiration date
+				$share = $this->validateExpirationDateInternal($share);
+			} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
+				$this->groupCreateChecks($share);
 
-			/*
-			 * For now ignore a set token.
-			 */
-			$share->setToken(
-				$this->secureRandom->generate(
-					\OC\Share\Constants::TOKEN_LENGTH,
-					\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
-				)
-			);
+				//Verify the expiration date
+				$share = $this->validateExpirationDateInternal($share);
+			} elseif ($share->getShareType() === IShare::TYPE_LINK) {
+				$this->linkCreateChecks($share);
+				$this->setLinkParent($share);
+
+				/*
+				 * For now ignore a set token.
+				 */
+				$share->setToken(
+					$this->secureRandom->generate(
+						\OC\Share\Constants::TOKEN_LENGTH,
+						\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
+					)
+				);
 
-			//Verify the expiration date
-			$share = $this->validateExpirationDate($share);
+				//Verify the expiration date
+				$share = $this->validateExpirationDate($share);
 
-			//Verify the password
-			$this->verifyPassword($share->getPassword());
+				//Verify the password
+				$this->verifyPassword($share->getPassword());
 
-			// If a password is set. Hash it!
-			if ($share->getPassword() !== null) {
-				$share->setPassword($this->hasher->hash($share->getPassword()));
+				// If a password is set. Hash it!
+				if ($share->getPassword() !== null) {
+					$share->setPassword($this->hasher->hash($share->getPassword()));
+				}
+			} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
+				$share->setToken(
+					$this->secureRandom->generate(
+						\OC\Share\Constants::TOKEN_LENGTH,
+						\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
+					)
+				);
 			}
-		} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
-			$share->setToken(
-				$this->secureRandom->generate(
-					\OC\Share\Constants::TOKEN_LENGTH,
-					\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
-				)
-			);
-		}
 
-		// Cannot share with the owner
-		if ($share->getShareType() === IShare::TYPE_USER &&
-			$share->getSharedWith() === $share->getShareOwner()) {
-			throw new \InvalidArgumentException('Can’t share with the share owner');
-		}
+			// Cannot share with the owner
+			if ($share->getShareType() === IShare::TYPE_USER &&
+				$share->getSharedWith() === $share->getShareOwner()) {
+				throw new \InvalidArgumentException('Can’t share with the share owner');
+			}
 
-		// Generate the target
-		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
-		$target = \OC\Files\Filesystem::normalizePath($target);
-		$share->setTarget($target);
+			// Generate the target
+			$target = $this->config->getSystemValue('share_folder', '/') . '/' . $share->getNode()->getName();
+			$target = \OC\Files\Filesystem::normalizePath($target);
+			$share->setTarget($target);
 
-		// Pre share event
-		$event = new GenericEvent($share);
-		$this->legacyDispatcher->dispatch('OCP\Share::preShare', $event);
-		if ($event->isPropagationStopped() && $event->hasArgument('error')) {
-			throw new \Exception($event->getArgument('error'));
-		}
+			// Pre share event
+			$event = new GenericEvent($share);
+			$this->legacyDispatcher->dispatch('OCP\Share::preShare', $event);
+			if ($event->isPropagationStopped() && $event->hasArgument('error')) {
+				throw new \Exception($event->getArgument('error'));
+			}
 
-		$oldShare = $share;
-		$provider = $this->factory->getProviderForType($share->getShareType());
-		$share = $provider->create($share);
-		//reuse the node we already have
-		$share->setNode($oldShare->getNode());
+			$oldShare = $share;
+			$provider = $this->factory->getProviderForType($share->getShareType());
+			$share = $provider->create($share);
+			//reuse the node we already have
+			$share->setNode($oldShare->getNode());
 
-		// Reset the target if it is null for the new share
-		if ($share->getTarget() === '') {
-			$share->setTarget($target);
+			// Reset the target if it is null for the new share
+			if ($share->getTarget() === '') {
+				$share->setTarget($target);
+			}
+		} catch (AlreadySharedException $e) {
+			// if a share for the same target already exists, dont create a new one, but do trigger the hooks and notifications again
+			$share = $e->getExistingShare();
 		}
 
 		// Post share event
diff --git a/lib/public/Share/Exceptions/AlreadySharedException.php b/lib/public/Share/Exceptions/AlreadySharedException.php
new file mode 100644
index 0000000000000000000000000000000000000000..2f8b92573876d3633cfeea33b1becf9d1e5f2f6f
--- /dev/null
+++ b/lib/public/Share/Exceptions/AlreadySharedException.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2021 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Share\Exceptions;
+
+use OCP\Share\IShare;
+
+/**
+ * @since 22.0.0
+ */
+class AlreadySharedException extends GenericShareException {
+	/** @var IShare */
+	private $existingShare;
+
+	/**
+	 * @since 22.0.0
+	 */
+	public function __construct(string $message, IShare $existingShare) {
+		parent::__construct($message);
+
+		$this->existingShare = $existingShare;
+	}
+
+	/**
+	 * @since 22.0.0
+	 */
+	public function getExistingShare(): IShare {
+		return $this->existingShare;
+	}
+}