diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index df752c90207c38a5c9c8e3451f1ecb0ce4ed3470..ff92cc8789241ebb65ec52680fa4876c253ff26f 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -4659,4 +4659,16 @@ int ast_channel_feature_hooks_append(struct ast_channel *chan, struct ast_bridge
  */
 int ast_channel_feature_hooks_replace(struct ast_channel *chan, struct ast_bridge_features *features);
 
+enum ast_channel_error {
+	/* Unable to determine what error occurred. */
+	AST_CHANNEL_ERROR_UNKNOWN,
+	/* Channel with this ID already exists */
+	AST_CHANNEL_ERROR_ID_EXISTS,
+};
+
+/*!
+ * \brief Get error code for latest channel operation.
+ */
+enum ast_channel_error ast_channel_errno(void);
+
 #endif /* _ASTERISK_CHANNEL_H */
diff --git a/include/asterisk/channel_internal.h b/include/asterisk/channel_internal.h
index d1231b400a699e5eb1dcb609c8ad2dcb3c71c05c..2316e2f24c9e128860ecf15071e07b895abafaee 100644
--- a/include/asterisk/channel_internal.h
+++ b/include/asterisk/channel_internal.h
@@ -25,3 +25,5 @@ int ast_channel_internal_is_finalized(struct ast_channel *chan);
 void ast_channel_internal_cleanup(struct ast_channel *chan);
 int ast_channel_internal_setup_topics(struct ast_channel *chan);
 
+void ast_channel_internal_errno_set(enum ast_channel_error error);
+enum ast_channel_error ast_channel_internal_errno(void);
diff --git a/main/channel.c b/main/channel.c
index 6804496f4128a4ce7fb933b3bf86e702146d18c2..4a9fe72a86a1b56f576f8f69d4fe12c22c3ceda0 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -770,6 +770,27 @@ static const struct ast_channel_tech null_tech = {
 
 static void ast_channel_destructor(void *obj);
 static void ast_dummy_channel_destructor(void *obj);
+static int ast_channel_by_uniqueid_cb(void *obj, void *arg, void *data, int flags);
+
+static int does_id_conflict(const char *uniqueid)
+{
+	struct ast_channel *conflict;
+	int length = 0;
+
+	if (ast_strlen_zero(uniqueid)) {
+		return 0;
+	}
+
+	conflict = ast_channel_callback(ast_channel_by_uniqueid_cb, (char *) uniqueid, &length, OBJ_NOLOCK);
+	if (conflict) {
+		ast_log(LOG_ERROR, "Channel Unique ID '%s' already in use by channel %s(%p)\n",
+			uniqueid, ast_channel_name(conflict), conflict);
+		ast_channel_unref(conflict);
+		return 1;
+	}
+
+	return 0;
+}
 
 /*! \brief Create a new channel structure */
 static struct ast_channel * attribute_malloc __attribute__((format(printf, 15, 0)))
@@ -945,16 +966,33 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
 		ast_channel_tech_set(tmp, &null_tech);
 	}
 
-	ast_channel_internal_finalize(tmp);
-
-	ast_atomic_fetchadd_int(&chancount, +1);
-
 	/* You might scream "locking inversion" at seeing this but it is actually perfectly fine.
 	 * Since the channel was just created nothing can know about it yet or even acquire it.
 	 */
 	ast_channel_lock(tmp);
 
-	ao2_link(channels, tmp);
+	ao2_lock(channels);
+
+	if (assignedids && (does_id_conflict(assignedids->uniqueid) || does_id_conflict(assignedids->uniqueid2))) {
+		ast_channel_internal_errno_set(AST_CHANNEL_ERROR_ID_EXISTS);
+		ao2_unlock(channels);
+		/* This is a bit unorthodox, but we can't just call ast_channel_stage_snapshot_done()
+		 * because that will result in attempting to publish the channel snapshot. That causes
+		 * badness in some places, such as CDRs. So we need to manually clear the flag on the
+		 * channel that says that a snapshot is being cleared.
+		 */
+		ast_clear_flag(ast_channel_flags(tmp), AST_FLAG_SNAPSHOT_STAGE);
+		ast_channel_unlock(tmp);
+		return ast_channel_unref(tmp);
+	}
+
+	ast_channel_internal_finalize(tmp);
+
+	ast_atomic_fetchadd_int(&chancount, +1);
+
+	ao2_link_flags(channels, tmp, OBJ_NOLOCK);
+
+	ao2_unlock(channels);
 
 	if (endpoint) {
 		ast_endpoint_add_channel(endpoint, tmp);
@@ -10866,3 +10904,8 @@ int ast_channel_feature_hooks_replace(struct ast_channel *chan, struct ast_bridg
 {
 	return channel_feature_hooks_set_full(chan, features, 1);
 }
+
+enum ast_channel_error ast_channel_errno(void)
+{
+	return ast_channel_internal_errno();
+}
diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c
index d94b267e6806955cfec7527d0d1a51b800c072f3..3c156d4fa211556aa0f1ff1a622b730399ba979b 100644
--- a/main/channel_internal_api.c
+++ b/main/channel_internal_api.c
@@ -1661,3 +1661,25 @@ int ast_channel_internal_setup_topics(struct ast_channel *chan)
 
 	return 0;
 }
+
+AST_THREADSTORAGE(channel_errno);
+
+void ast_channel_internal_errno_set(enum ast_channel_error error)
+{
+	enum ast_channel_error *error_code = ast_threadstorage_get(&channel_errno, sizeof(*error_code));
+	if (!error_code) {
+		return;
+	}
+
+	*error_code = error;
+}
+
+enum ast_channel_error ast_channel_internal_errno(void)
+{
+	enum ast_channel_error *error_code = ast_threadstorage_get(&channel_errno, sizeof(*error_code));
+	if (!error_code) {
+		return AST_CHANNEL_ERROR_UNKNOWN;
+	}
+
+	return *error_code;
+}
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index 8d32921298787055e6629398319e30f4bbbefdf2..b00b2378d6610d9c139a9a86da611372832a9167 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -1230,7 +1230,12 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
 	}
 
 	if (ast_dial_prerun(dial, other, format_cap)) {
-		ast_ari_response_alloc_failed(response);
+		if (ast_channel_errno() == AST_CHANNEL_ERROR_ID_EXISTS) {
+			ast_ari_response_error(response, 409, "Conflict",
+				"Channel with given unique ID already exists");
+		} else {
+			ast_ari_response_alloc_failed(response);
+		}
 		ast_dial_destroy(dial);
 		ast_free(origination);
 		ast_channel_cleanup(other);
diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c
index 25da17d4d961ddf1e1e9bc4bb3553220276b83ac..b7c088c4d1a9ba2ecbd8d7d94c7b835d7daa15c9 100644
--- a/res/res_ari_channels.c
+++ b/res/res_ari_channels.c
@@ -253,6 +253,7 @@ static void ast_ari_channels_originate_cb(
 	case 500: /* Internal Server Error */
 	case 501: /* Not Implemented */
 	case 400: /* Invalid parameters for originating a channel. */
+	case 409: /* Channel with given unique ID already exists. */
 		is_valid = 1;
 		break;
 	default:
@@ -615,6 +616,7 @@ static void ast_ari_channels_originate_with_id_cb(
 	case 500: /* Internal Server Error */
 	case 501: /* Not Implemented */
 	case 400: /* Invalid parameters for originating a channel. */
+	case 409: /* Channel with given unique ID already exists. */
 		is_valid = 1;
 		break;
 	default:
diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json
index ee18bfe119683d5d563b4e1d797cce22b2f0cd73..60ace075d365d8098c9dc5827a81884c931a7a5a 100644
--- a/rest-api/api-docs/channels.json
+++ b/rest-api/api-docs/channels.json
@@ -142,6 +142,10 @@
 						{
 							"code": 400,
 							"reason": "Invalid parameters for originating a channel."
+						},
+						{
+							"code": 409,
+							"reason": "Channel with given unique ID already exists."
 						}
 					]
 				}
@@ -368,6 +372,10 @@
 						{
 							"code": 400,
 							"reason": "Invalid parameters for originating a channel."
+						},
+						{
+							"code": 409,
+							"reason": "Channel with given unique ID already exists."
 						}
 					]