diff --git a/src/lib/Util.py b/src/lib/Util.py
index cb55e807a0c0c081f17fb091e06df051837a719e..8819db38bc3fbbc342bd7e9de6d1f4abcb6c3607 100644
--- a/src/lib/Util.py
+++ b/src/lib/Util.py
@@ -272,6 +272,13 @@ def dgetopt(arglist, opt, vopt, msg):
             ret[vopt[option]] = garg
     return ret, list(args)
 
+# regexes for merging nodelists:
+cluster_node_reg = re.compile(r'(\D+)(\d+)')
+device_reg = re.compile(r'(\D+\d+-\D+)(\d)')
+cray_loc = re.compile(r'^(\d+)')
+node_and_dev = re.compile(r'(\D+\d+)-(\D+[\[\]\-\d]+)')
+first_number_reg = re.compile(r'(\d+)')
+
 def merge_nodelist(locations):
     '''create a set of dashed-ranges from a node list
 
@@ -283,9 +290,6 @@ def merge_nodelist(locations):
         list.
 
     '''
-    reg = re.compile(r'(\D+)(\d+)')
-    device_reg = re.compile(r'(\D+\d+-\D+)(\d)')
-    cray_loc = re.compile(r'^(\d+)')
     # create a dictionary with a key for each node prefix,
     # with sorted lists of node numbers as the values
     noderanges = {}
@@ -296,7 +300,7 @@ def merge_nodelist(locations):
     ret = []
 
     if (len(locations) > 1 and
-        reg.match(locations[0]) is None and
+        cluster_node_reg.match(locations[0]) is None and
         device_reg.match(locations[0]) is None and
         cray_loc.match(locations[0]) is None):
         # We don't know how to merge this list of locations.
@@ -310,12 +314,12 @@ def merge_nodelist(locations):
 
     for name in locations:
         try:
-            prefix = reg.match(name).group(1)
-            nodenum = reg.match(name).group(2)
-            if device_reg.match(name):
+            dev_match = device_reg.match(name)
+            if dev_match:
                 has_device_naming = True
-                prefix = device_reg.match(name).group(1)
-                nodenum = device_reg.match(name).group(2)
+                prefix, nodenum = dev_match.groups()
+            else:
+                prefix, nodenum = cluster_node_reg.match(name).groups()
             num_digits = len(nodenum)
             newnum = int(nodenum)
             if not prefix in noderanges:
@@ -345,7 +349,8 @@ def merge_nodelist(locations):
             prefix_format_str[prefix] = {'compact': "[%s%s-%s]",
                                          'single': "%s%s"}
 
-    ret = _gen_compacted_list(noderanges, prefix_format_str)
+    # may have some node names from above
+    ret.extend(_gen_compacted_list(noderanges, prefix_format_str))
 
     # Compact Nodes and Devices
     if has_device_naming:
@@ -359,19 +364,20 @@ def _compact_device_list(locations):
     this is a second stage for the more complex device-split locations
     '''
     ret = []
-    node_and_dev = re.compile(r'(\D+\d+)-(\D+[\[\]\-\d]+)')
     nodes_by_dev = {}
     for node in sorted(locations):
         match = node_and_dev.match(node)
-        if match is None:
-            ret.append(node)
-        else:
-            node_name = match.group(1)
-            dev_set = match.group(2)
-            if nodes_by_dev.get(dev_set, None):
-                nodes_by_dev[dev_set].append(node_name)
+        try:
+            if match is None:
+                ret.append(node)
             else:
-                nodes_by_dev[dev_set] = [node_name]
+                node_name, dev_set = match.groups()
+                if nodes_by_dev.get(dev_set, None):
+                    nodes_by_dev[dev_set].append(node_name)
+                else:
+                    nodes_by_dev[dev_set] = [node_name]
+        except AttributeError:
+            ret.append(node)
     for dev_set, nodes in nodes_by_dev.items():
         compacted_nodes = merge_nodelist(nodes).split(',')
         ret.extend(["%s-%s" % (node, dev_set) for node in compacted_nodes])
@@ -379,9 +385,16 @@ def _compact_device_list(locations):
 
 def _compare_on_low_range(left, right):
     '''comparison to sort a compressed list'''
-    first_number_reg = re.compile(r'(\d+)')
-    left_num = first_number_reg.search(left).group(1)
-    right_num = first_number_reg.search(right).group(1)
+    try:
+        left_num = first_number_reg.search(left).group(1)
+        right_num = first_number_reg.search(right).group(1)
+    except AttributeError:
+        # Didn't have a number with this particular node, fall back to lexical.
+        if left == right:
+            return 0
+        elif left > right:
+            return 1
+        return -1
     if left_num == right_num:
         return 0
     elif left_num > right_num:
diff --git a/testsuite/TestCobalt/TestUtil.py b/testsuite/TestCobalt/TestUtil.py
index 1735cf877398db0a5cd73fe92dcbcf5936196c75..791f89dd422b4ac2e244d3c85ec5da8d0f04fc8f 100644
--- a/testsuite/TestCobalt/TestUtil.py
+++ b/testsuite/TestCobalt/TestUtil.py
@@ -411,6 +411,13 @@ class TestMergeNodelist(object):
             "cc01.test", "cc44.test"])
         assert_match(resp, correct, "Incorrect merged list")
 
+    def test_merge_nodelist_cluster_with_oneoff(self):
+        '''cluster system style full nodes with one-off nonconforming node name'''
+        correct = 'bombadil,cc01,cc[42-44],frodo'
+        resp = Cobalt.Util.merge_nodelist(["cc43.test", "cc42.test",
+            "cc01.test", "cc44.test", "frodo", "bombadil"])
+        assert_match(resp, correct, "Incorrect merged list")
+
     def test_merge_nodelist_cluster_gpu(self):
         '''cluster system style gpu-naming from single node'''
         correct = 'cc01-gpu[0-3]'
@@ -460,3 +467,18 @@ class TestMergeNodelist(object):
                 ]
         resp = Cobalt.Util.merge_nodelist(location)
         assert_match(resp, correct, "Incorrect merged list")
+
+    def test_merge_nodelist_cluster_gpu_oneoff(self):
+        '''cluster system style gpu-naming mixed with standard and one-off nodes'''
+        correct = 'bombadil,cc01,cc[01-03]-gpu[0-3],cc04-gpu[4-7],cc05-gpu[0-3],cc06-gpu0,cc07,cc[08-09]-gpu5,cc[10-12],cc42'
+        location = ['cc01-gpu0', 'cc01-gpu1', 'cc01-gpu2', 'cc01-gpu3',
+                    'cc02-gpu0', 'cc02-gpu1', 'cc02-gpu2', 'cc02-gpu3',
+                    'cc10', 'cc11', 'cc12', 'cc42',
+                    'cc05-gpu0', 'cc05-gpu1', 'cc05-gpu2', 'cc05-gpu3',
+                    'cc03-gpu0', 'cc03-gpu1', 'cc03-gpu2', 'cc03-gpu3',
+                    'cc04-gpu4', 'cc04-gpu5', 'cc04-gpu6', 'cc04-gpu7',
+                    'cc06-gpu0', 'cc09-gpu5', 'cc08-gpu5', 'cc01', 'cc07',
+                    'bombadil',
+                ]
+        resp = Cobalt.Util.merge_nodelist(location)
+        assert_match(resp, correct, "Incorrect merged list")