diff --git a/fsl/data/featanalysis.py b/fsl/data/featanalysis.py
index 75fe43a61a9d2a58347455f2e50e46e3ea9e9b60..c1fd09557903580e36ed6d3de26a04d3dc68da1c 100644
--- a/fsl/data/featanalysis.py
+++ b/fsl/data/featanalysis.py
@@ -90,7 +90,7 @@ def isFEATDir(path):
 
     try:
         fslimage.addExt(op.join(path, 'filtered_func_data'), mustExist=True)
-    except ValueError:
+    except fslimage.PathError:
         return False
     
     if not op.exists(op.join(dirname, 'design.fsf')): return False
@@ -451,7 +451,7 @@ def getDataFile(featdir):
     """Returns the name of the file in the FEAT directory which contains
     the model input data (typically called ``filtered_func_data.nii.gz``).
 
-    Raises a :exc:`ValueError` if the file does not exist.
+    Raises a :exc:`.PathError` if the file does not exist.
 
     :arg featdir: A FEAT directory.
     """
@@ -464,7 +464,7 @@ def getMelodicFile(featdir):
     components (if melodic ICA was performed as part of the FEAT
     analysis). This file can be loaded as a :class:`.MelodicImage`.
 
-    Raises a :exc:`ValueError` if the file does not exist.
+    Raises a :exc:`.PathError` if the file does not exist.
     """
     melfile = op.join(featdir, 'filtered_func_data.ica', 'melodic_IC')
     return fslimage.addExt(melfile, mustExist=True)
@@ -474,7 +474,7 @@ def getResidualFile(featdir):
     """Returns the name of the file in the FEAT results which contains
     the model fit residuals (typically called ``res4d.nii.gz``).
 
-    Raises a :exc:`ValueError` if the file does not exist.
+    Raises a :exc:`.PathError` if the file does not exist.
 
     :arg featdir: A FEAT directory.
     """
@@ -485,7 +485,7 @@ def getResidualFile(featdir):
 def getPEFile(featdir, ev):
     """Returns the path of the PE file for the specified EV.
 
-    Raises a :exc:`ValueError` if the file does not exist.
+    Raises a :exc:`.PathError` if the file does not exist.
 
     :arg featdir: A FEAT directory.
     :arg ev:      The EV number (0-indexed).
@@ -497,7 +497,7 @@ def getPEFile(featdir, ev):
 def getCOPEFile(featdir, contrast):
     """Returns the path of the COPE file for the specified contrast.
 
-    Raises a :exc:`ValueError` if the file does not exist.
+    Raises a :exc:`.PathError` if the file does not exist.
 
     :arg featdir:  A FEAT directory.
     :arg contrast: The contrast number (0-indexed). 
@@ -509,7 +509,7 @@ def getCOPEFile(featdir, contrast):
 def getZStatFile(featdir, contrast):
     """Returns the path of the Z-statistic file for the specified contrast.
 
-    Raises a :exc:`ValueError` if the file does not exist.
+    Raises a :exc:`.PathError` if the file does not exist.
 
     :arg featdir:  A FEAT directory.
     :arg contrast: The contrast number (0-indexed). 
@@ -521,7 +521,7 @@ def getZStatFile(featdir, contrast):
 def getClusterMaskFile(featdir, contrast):
     """Returns the path of the cluster mask file for the specified contrast.
 
-    Raises a :exc:`ValueError` if the file does not exist.
+    Raises a :exc:`.PathError` if the file does not exist.
 
     :arg featdir:  A FEAT directory.
     :arg contrast: The contrast number (0-indexed). 
diff --git a/fsl/data/image.py b/fsl/data/image.py
index 696ebd855a92b0dd67b0a257cef5e9ab2193db13..384768aab24bbfdbf3404e2b2292f28c9461cd90 100644
--- a/fsl/data/image.py
+++ b/fsl/data/image.py
@@ -769,10 +769,22 @@ EXTENSION_DESCRIPTIONS = ['Compressed NIFTI images',
 """Descriptions for each of the extensions in :data:`ALLOWED_EXTENSIONS`. """
 
 
+REPLACEMENTS = {'.hdr' : ['.img', '.img.gz']}
+"""Suffix replacements used by :func:`addExt` to resolve file path
+ambiguities - see :func:`fsl.utils.path.addExt`.
+"""
+
+
 DEFAULT_EXTENSION  = '.nii.gz'
 """The default file extension (TODO read this from ``$FSLOUTPUTTYPE``)."""
 
 
+PathError = fslpath.PathError
+"""Error raised by :mod:`fsl.utils.path` functions when an error occurs.
+Made available in this module for convenience.
+"""
+
+
 def looksLikeImage(filename, allowedExts=None):
     """Returns ``True`` if the given file looks like an image, ``False``
     otherwise.
@@ -810,7 +822,8 @@ def addExt(prefix, mustExist=True):
     return fslpath.addExt(prefix,
                           ALLOWED_EXTENSIONS,
                           mustExist,
-                          DEFAULT_EXTENSION)
+                          DEFAULT_EXTENSION,
+                          replace=REPLACEMENTS)
 
 
 def loadIndexedImageFile(filename):
diff --git a/fsl/data/melodicanalysis.py b/fsl/data/melodicanalysis.py
index 9de847d439741b64a980bf34355dcca1693bdbfe..6bb63b7000578ea36c084e1ef9eb2e2d0cfaf5ec 100644
--- a/fsl/data/melodicanalysis.py
+++ b/fsl/data/melodicanalysis.py
@@ -77,7 +77,7 @@ def isMelodicDir(path):
     # Must contain an image file called melodic_IC
     try:
         fslimage.addExt(op.join(dirname, 'melodic_IC'), mustExist=True)
-    except ValueError:
+    except fslimage.PathError:
         return False
 
     # Must contain files called
@@ -124,8 +124,8 @@ def getDataFile(meldir):
 
     dataFile = op.join(topDir, 'filtered_func_data')
 
-    try:               return fslimage.addExt(dataFile, mustExist=True)
-    except ValueError: return None
+    try:                       return fslimage.addExt(dataFile, mustExist=True)
+    except fslimage.PathErrpr: return None
 
 
 def getMeanFile(meldir):
diff --git a/fsl/utils/path.py b/fsl/utils/path.py
index 408b77c23ba7eed3579c7d4876caa5052dce546a..ef628918c1df60ce98985a69879a73d52a394153 100644
--- a/fsl/utils/path.py
+++ b/fsl/utils/path.py
@@ -15,12 +15,18 @@ paths.
    shallowest
    addExt
    removeExt
+   getExt
 """
 
 
 import os.path as op
 
 
+class PathError(Exception):
+    """``Exception`` class raised by :func:`addExt` and :func:`getExt`. """
+    pass
+
+
 def deepest(path, suffixes):
     """Finds the deepest directory which ends with one of the given
     sequence of suffixes, or returns ``None`` if no directories end
@@ -64,7 +70,11 @@ def shallowest(path, suffixes):
     return None 
 
 
-def addExt(prefix, allowedExts, mustExist=True, defaultExt=None):
+def addExt(prefix,
+           allowedExts,
+           mustExist=True,
+           defaultExt=None,
+           replace=None):
     """Adds a file extension to the given file ``prefix``.
 
     If ``mustExist`` is False, and the file does not already have a 
@@ -77,17 +87,47 @@ def addExt(prefix, allowedExts, mustExist=True, defaultExt=None):
     extension.  A :exc:`ValueError` is raised if:
 
        - No files exist with the given prefix and a supported extension.
-       - More than one file exists with the given prefix, and a supported
-         extension.
+    
+       - ``replace`` is ``None``, and more than one file exists with the
+         given prefix, and a supported extension. 
 
     Otherwise the full file name is returned.
 
-    :arg prefix:      The file name refix to modify.
-    :arg mustExist:   Whether the file must exist or not.
+    :arg prefix:      The file name prefix to modify.
+
     :arg allowedExts: List of allowed file extensions.
+    
+    :arg mustExist:   Whether the file must exist or not.
+    
     :arg defaultExt:  Default file extension to use.
+    
+    :arg replace:     If multiple files exist with the same ``prefix`` and
+                      supported extensions (e.g. ``file.hdr`` and
+                      ``file.img``), this dictionary can be used to resolve
+                      ambiguities. It must have the structure::
+
+                          {
+                              suffix : [replacement, ...],
+                              ...
+                          }
+    
+                      If files with ``suffix`` and one of the ``replacement``
+                      suffixes exists, the ``suffix`` file will
+                      be ignored, and replaced with the ``replacement`` file.
+                      If multiple ``replacement`` files exist alongside the
+                      ``suffix`` file, a ``PathError`` is raised.
+
+    .. note:: The primary use-case of the ``replace`` parameter is to resolve
+              ambiguity with respect to NIFTI and ANALYSE75 image pairs. By
+              specifying ``replace={'.hdr' : ['.img'. '.img.gz'}``, the
+              ``addExt`` function is able to figure out what you mean when you
+              wish to add an extension to ``file``, and ``file.hdr`` and
+              either ``file.img`` or ``file.img.gz`` (but not both) exist.
     """
 
+    if replace is None:
+        replace = {}
+
     if not mustExist:
 
         # the provided file name already
@@ -101,31 +141,74 @@ def addExt(prefix, allowedExts, mustExist=True, defaultExt=None):
     # If the provided prefix already ends with a
     # supported extension , check to see that it exists
     if any([prefix.endswith(ext) for ext in allowedExts]):
-        extended = [prefix]
+        allPaths = [prefix]
         
     # Otherwise, make a bunch of file names, one per
     # supported extension, and test to see if exactly
     # one of them exists.
     else:
-        extended = [prefix + ext for ext in allowedExts]
+        allPaths = [prefix + ext for ext in allowedExts]
 
-    exists = [op.isfile(e) for e in extended]
+    exists  = [op.isfile(e) for e in allPaths]
+    nexists = sum(exists)
 
     # Could not find any supported file
     # with the specified prefix
-    if not any(exists):
-        raise ValueError(
-            'Could not find a supported file with prefix {}'.format(prefix))
+    if nexists == 0:
+        raise PathError('Could not find a supported file '
+                        'with prefix {}'.format(prefix))
 
     # Ambiguity! More than one supported
-    # file with the specified prefix
-    if sum(exists) > 1:
-        raise ValueError('More than one file with prefix {}'.format(prefix))
+    # file with the specified prefix.
+    elif nexists > 1:
+
+        # Remove non-existent paths from the
+        # extended list, get all their
+        # suffixes, and potential replacements
+        allPaths     = [allPaths[i] for i in range(len(allPaths)) if exists[i]]
+        suffixes     = [getExt(e, allowedExts) for e in allPaths]
+        replacements = [replace.get(s) for s in suffixes]
+        hasReplace   = [r is not None for r in replacements]
+
+        for p, r in zip(allPaths, replacements):
+            print '   {} replacements: {}'.format(p, r)
+
+        # If any replacement has been specified
+        # for any of the existing suffixes,
+        # see if we have a unique match for
+        # exactly one existing suffix, the
+        # one to be ignored/replaced. 
+        if sum(hasReplace) == 1:
+
+            # Make sure there is exactly one potential
+            # replacement for this suffix. If there's
+            # more than one (e.g. file.hdr plus both
+            # file.img and file.img.gz) we can't resolve
+            # the ambiguity. In this case the code will
+            # fall through to the raise statement below.
+            toReplace    = allPaths[hasReplace.index(True)]
+            replacements = replacements[hasReplace.index(True)]
+            replacements = [prefix + ext for ext in replacements]
+            replExists   = [r in allPaths for r in replacements]
+
+            if sum(replExists) == 1:
+                
+                replacedBy = replacements[replExists.index(True)]
+                allPaths[allPaths.index(toReplace)] = replacedBy
+                allPaths = list(set(allPaths))
+
+        exists = [True] * len(allPaths)
+
+        # There's more than one path match -
+        # we can't resolve the ambiguity
+        if len(allPaths) > 1:
+            raise PathError('More than one file with '
+                            'prefix {}'.format(prefix))
 
     # Return the full file name of the
     # supported file that was found
     extIdx = exists.index(True)
-    return extended[extIdx]
+    return allPaths[extIdx]
 
 
 def removeExt(filename, allowedExts):
@@ -151,3 +234,32 @@ def removeExt(filename, allowedExts):
 
     # and trim it from the file name
     return filename[:-extLen]
+
+
+def getExt(filename, allowedExts=None):
+    """Returns the extension from the given file name.
+
+    If ``allowedExts`` is ``None``, this function is equivalent to using::
+    
+        os.path.splitext(filename)[1]
+
+    If ``allowedExts`` is provided, but the file does not end with an allowed
+    extension, a :exc:`PathError` is raised.
+
+    :arg allowedExts: Allowed/recognised file extensions.
+    """
+
+    # If allowedExts is not specified,
+    # we just use op.splitext
+    if allowedExts is None:
+        return op.splitext(filename)[1]
+
+    # Otherwise, try and find a suffix match
+    extMatches = [filename.endswith(ext) for ext in allowedExts]
+
+    if not any(extMatches):
+        raise PathError('{} does not end in a supported extension ({})'.format(
+            filename, ', '.join(allowedExts)))
+
+    extIdx = extMatches.index(True)
+    return allowedExts[extIdx]