diff --git a/modules/filter/filter.admin.inc b/modules/filter/filter.admin.inc
index e2a6fde4ae372252bfb12c3ca816d29cda655f05..0b053eef70d9a7945dda89829ab069983c7287f1 100644
--- a/modules/filter/filter.admin.inc
+++ b/modules/filter/filter.admin.inc
@@ -136,7 +136,7 @@ function filter_admin_format_form($form, &$form_state, $format) {
     '#default_value' => $format->format,
     '#maxlength' => 255,
     '#machine_name' => array(
-      'exists' => 'filter_format_load',
+      'exists' => 'filter_format_exists',
     ),
     '#disabled' => !empty($format->format),
   );
@@ -288,13 +288,16 @@ function theme_filter_admin_format_filter_order($variables) {
  * Validate text format form submissions.
  */
 function filter_admin_format_form_validate($form, &$form_state) {
-  if (!isset($form_state['values']['format'])) {
-    $format_name = trim($form_state['values']['name']);
-    form_set_value($form['name'], $format_name, $form_state);
-    $result = db_query("SELECT format FROM {filter_format} WHERE name = :name", array(':name' => $format_name))->fetchField();
-    if ($result) {
-      form_set_error('name', t('Text format names must be unique. A format named %name already exists.', array('%name' => $format_name)));
-    }
+  $format_format = trim($form_state['values']['format']);
+  $format_name = trim($form_state['values']['name']);
+
+  // Ensure that the values to be saved later are exactly the ones validated.
+  form_set_value($form['format'], $format_format, $form_state);
+  form_set_value($form['name'], $format_name, $form_state);
+
+  $result = db_query("SELECT format FROM {filter_format} WHERE name = :name AND format <> :format", array(':name' => $format_name, ':format' => $format_format))->fetchField();
+  if ($result) {
+    form_set_error('name', t('Text format names must be unique. A format named %name already exists.', array('%name' => $format_name)));
   }
 }
 
@@ -307,9 +310,6 @@ function filter_admin_format_form_submit($form, &$form_state) {
 
   // Save text format.
   $format = (object) $form_state['values'];
-  if (!isset($form_state['values']['format'])) {
-    $format->format = NULL;
-  }
   $status = filter_format_save($format);
 
   // Save user permissions.
diff --git a/modules/filter/filter.module b/modules/filter/filter.module
index e64bee41dea2caf772c761101646b6bfe07979a1..617987421e53a0655872e5f8c2f987d5275c4915 100644
--- a/modules/filter/filter.module
+++ b/modules/filter/filter.module
@@ -153,7 +153,11 @@ function _filter_disable_format_access($format) {
  *   The format ID.
  *
  * @return
- *   A fully-populated text format object.
+ *   A fully-populated text format object, if the requested format exists and
+ *   is enabled. If the format does not exist, or exists in the database but
+ *   has been marked as disabled, FALSE is returned.
+ *
+ * @see filter_format_exists()
  */
 function filter_format_load($format_id) {
   $formats = filter_formats();
@@ -165,9 +169,10 @@ function filter_format_load($format_id) {
  *
  * @param $format
  *   A format object using the properties:
+ *   - 'format': A machine-readable name representing the ID of the text format
+ *     to save. If this corresponds to an existing text format, that format
+ *     will be updated; otherwise, a new format will be created.
  *   - 'name': The title of the text format.
- *   - 'format': (optional) The internal ID of the text format. If omitted, a
- *     new text format is created.
  *   - 'weight': (optional) The weight of the text format, which controls its
  *     placement in text format lists. If omitted, the weight is set to 0.
  *   - 'filters': (optional) An associative, multi-dimensional array of filters
@@ -266,9 +271,9 @@ function filter_format_save($format) {
  * Disable a text format.
  *
  * There is no core facility to re-enable a disabled format. It is not deleted
- * to keep information for contrib and to make sure the format auto increment
- * id is never reused. As there might be content using the disabled format,
- * this would lead to data corruption.
+ * to keep information for contrib and to make sure the format ID is never
+ * reused. As there might be content using the disabled format, this would lead
+ * to data corruption.
  *
  * @param $format
  *   The text format object to be disabled.
@@ -287,6 +292,23 @@ function filter_format_disable($format) {
   cache_clear_all($format->format . ':', 'cache_filter', TRUE);
 }
 
+/**
+ * Determines if a text format exists.
+ *
+ * @param $format_id
+ *   The ID of the text format to check.
+ *
+ * @return
+ *   TRUE if the text format exists, FALSE otherwise. Note that for disabled
+ *   formats filter_format_exists() will return TRUE while filter_format_load()
+ *   will return FALSE.
+ *
+ * @see filter_format_load()
+ */
+function filter_format_exists($format_id) {
+  return (bool) db_query_range('SELECT 1 FROM {filter_format} WHERE format = :format', 0, 1, array(':format' => $format_id))->fetchField();
+}
+
 /**
  * Display a text format form title.
  */
@@ -525,7 +547,7 @@ function filter_default_format($account = NULL) {
 function filter_fallback_format() {
   // This variable is automatically set in the database for all installations
   // of Drupal. In the event that it gets disabled or deleted somehow, there
-  // is no safe default to return, since we do not want to risk making an 
+  // is no safe default to return, since we do not want to risk making an
   // existing (and potentially unsafe) text format on the site automatically
   // available to all users. Returning NULL at least guarantees that this
   // cannot happen.
diff --git a/modules/filter/filter.test b/modules/filter/filter.test
index 8500846c417eb668be888c8b468c9ceca6e45f22..33c1b3a741304dd16991dafa2bb19382750d768b 100644
--- a/modules/filter/filter.test
+++ b/modules/filter/filter.test
@@ -207,6 +207,24 @@ class FilterAdminTestCase extends DrupalWebTestCase {
     // Verify that disabled text format no longer exists.
     $this->drupalGet('admin/config/content/formats/' . $format->format);
     $this->assertResponse(404, t('Disabled text format no longer exists.'));
+
+    // Attempt to create a format of the same machine name as the disabled
+    // format but with a different human readable name.
+    $edit = array(
+      'format' => $format->format,
+      'name' => 'New format',
+    );
+    $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration'));
+    $this->assertText('The machine-readable name is already in use. It must be unique.');
+
+    // Attempt to create a format of the same human readable name as the
+    // disabled format but with a different machine name.
+    $edit = array(
+      'format' => 'new_format',
+      'name' => $format->name,
+    );
+    $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration'));
+    $this->assertText('Text format names must be unique. A format named ' . check_plain($format->name) . ' already exists.');
   }
 
   /**
@@ -1082,7 +1100,7 @@ class FilterUnitTestCase extends DrupalUnitTestCase {
     $f = _filter_html('<p onerror="alert(0);" />', $filter);
     $this->assertNoNormalized($f, 'onerror', t('HTML filter should remove on* attributes on default.'));
 
-    $f = _filter_html('<code onerror>&nbsp;</code>', $filter);    
+    $f = _filter_html('<code onerror>&nbsp;</code>', $filter);
     $this->assertNoNormalized($f, 'onerror', t('HTML filter should remove empty on* attributes on default.'));
   }
 
diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info
index 9ca6b94f97231657141bb01d77d5fcb604669e8a..dded764eebda55f763c2e8a55c4ca4c9aac5938a 100644
--- a/modules/simpletest/simpletest.info
+++ b/modules/simpletest/simpletest.info
@@ -41,6 +41,7 @@ files[] = tests/update.test
 files[] = tests/xmlrpc.test
 files[] = tests/upgrade/upgrade.test
 files[] = tests/upgrade/upgrade.comment.test
+files[] = tests/upgrade/upgrade.filter.test
 files[] = tests/upgrade/upgrade.node.test
 files[] = tests/upgrade/upgrade.taxonomy.test
 files[] = tests/upgrade/upgrade.upload.test