I've got two Python scripts as a tools in a standard toolbox, and when the tools finish they will sometimes remove a layer from the MXD.

  • It is most likely to happen on the first run of the tool after opening the MXD.
  • It always happens to the same feature class, regardless of what it is called in the MXD or whose version it is in.
  • It will only remove one copy of the layer - if it has been copied multiple times the copies will remain.
  • The tool has no awareness of this layer, and it does not share the name of anything in the script.
  • It will happen regardless of where the layer is located in the TOC but seems to happen more often when it is at the top.
  • The issue has only been replicated on Windows 10 machines thus far.
  • The removal happens after the "tool finished at blah blah blah," right around the time it's adding the final timestamp.

The tool performs a selection based on a user-provided identifier and one script looks in hardcoded ("hardcoded") layers while the other looks in user-supplied layers ("user"). Neither looks at the dataframe/TOC, but "user" uses ListLayers. Neither is aware of the MXD or dataframe (meaning they are not set in environments or mapping or anything).

This is the only custom toolbox in the MXD but there are some controls with VBA code that DO look for that layer using GetLayer([featurename]) and not the layer name.

Here is the code for the hardcoded script:

# Sarah Schwarzer
# 20190725
# Purpose:
# User supplies ProjectID and selects utility categories, tool will select all features within those categories
# that match the selected ProjectID.
# Dependencies:
# Run through "F:\GIS\PROJECTS\ScriptingClub\SarahScripts\Testing.tbx\Select by ProjectID (User)" currently
# Requires MXD layer names to match the UtilityEditing MXD provided in F:\GIS\SOP\EditorSetup.
# Inputs:
# Built to work through ArcGIS toolbox GUI. User supplies a string of projectIDs separated by a semicolon and
# checks the boxes for each utility type they want to search.
# Outputs:
# None.
# To Do:
# No plans to support this tool in the future. Maybe fold the checkbox options into the user-supplied version.
# Installation:
# Add toolbox to your MXD and double click tool.

import arcpy

# Grab input from user
ProjStr = arcpy.GetParameterAsText(0) # a string of projectIDs separated by a semicolon
ischeckedSewer = arcpy.GetParameterAsText(1) # boolean passed as text. True indicates user wants to look in sewer features
ischeckedStorm = arcpy.GetParameterAsText(2) # boolean passed as text. True indicates user wants to look in storm features
ischeckedWater = arcpy.GetParameterAsText(3) # boolean passed as text. True indicates user wants to look in water features
ClearAll = arcpy.GetParameterAsText(4) # boolean passed as test. True indicates the user would like to clear the existing selection from the layers they supplied.

# List of the all the feature layers in each utility type. Requires names to match MXD.
sewerfcList = [r'SEWER\City\Sewer Facility',r'SEWER\City\Sewer Clean Out',r'SEWER\City\Sewer Lateral',r'SEWER\City\Sewer Fitting',r'SEWER\City\Sewer Meters',r'SEWER\City\Force Main Valve',r'SEWER\City\Sewer Manhole',r'SEWER\City\Sewer Control Valve', r'SEWER\City\Sewer System Valve', r'SEWER\City\Gravity Main',r'SEWER\City\Gravity Main (HPL)',r'SEWER\City\Force Main',r'SEWER\City\Sewer Casing']
stormfcList = [r'STORM\Stormwater Structures',r'STORM\Stormwater Pipes']
waterfcList = [r'WATER\Water System Facility',r'WATER\Water Clear Well',r'WATER\Water Meter',r'WATER\Fire Hydrant',r'WATER\Water Valve',r'WATER\Water Control Valve',r'WATER\Water Lateral',r'WATER\Water Main',r'WATER\Water Gravity Main',r'WATER\Water Main Casing',]

# Create a blank list then fill it with the selected utilities' feature layers
utilfcList = []

if str(ischeckedSewer) == 'true':
if str(ischeckedStorm) == 'true':
if str(ischeckedWater) == 'true':

# Create list of project IDs from the user's input
ProjList = [x.strip() for x in ProjStr.split(";")]

# If the user checked the box to clear the previous selection, clear the selections from everything that has a PROJECTID
if str(ClearAll) == 'true':
    for lyr in utilfcList:
        arcpy.SelectLayerByAttribute_management(lyr, "CLEAR_SELECTION")

#  The function that actually does the selection.
def make_selection(proj, layer):
    whereStr = "PROJECTID = '%s'" % (proj)  #  creates a string to supply as the where clause
    arcpy.SelectLayerByAttribute_management(layer, "ADD_TO_SELECTION", whereStr)

foundlist = [] # list of strings to report selected features by PROJECTID and feature layer
notfoundlist = []  # list of strings to report combinations of PROJECTID and feature layer that did NOT return results
LayerNotFound = [] # list of layers not found

for pr in ProjList: # for each PROJECTID supplied
    for u in utilfcList:  # and each feature class that contains the field PROJECTID
            begincount = int(arcpy.GetCount_management(u).getOutput(0)) # count the current features BEFORE making any new selections
            #  this will be the full feature count if there is no current selection, OR will be the number of total records selected after making selections
            make_selection(pr, u) #  do the thing!
            endcount = int(arcpy.GetCount_management(u).getOutput(0)) # total count AFTER the selection
            if endcount == 0: # this happens when there are no selections made period. why does it sometimes show ALL records?
                count = 0
                notfoundlist.append("{1}: {0}.".format(u, pr)) # stores a formatted string to a list to be printed as results later
                if begincount > endcount: # catches the first round through when the begincount is the total number of features in the layer
                    count = endcount  # basically says only the last number is needed
                    count = endcount - begincount # catches everything on the 2nd or greater project where selections already exist
                foundlist.append("{1}: {0} - {2} records selected.".format(u, pr, count, begincount, endcount)) # stores a formatted string to a list to be printed as results later
            LayerNotFound.append("{0} not found or unable to perform selection.".format(u))

# report missing layers
if len(LayerNotFound)>0:
    arcpy.AddMessage("\nThe following layers were not found:")
    for e in LayerNotFound:
    arcpy.AddError("No records found.")

# report selections
if len(foundlist)>0:
    arcpy.AddMessage("\nThe following selections were made:")
    for f in foundlist:

# report feature layers that have PROJECTID field but no features were found
if len(notfoundlist)>0:
    arcpy.AddMessage("\nThe following PROJECTID and feature class combos did not return any results:")
    for n in notfoundlist:

closed as unclear what you're asking by Hornbydd, Fran Raga, whyzar, xunilk, Jochen Schwarze Aug 2 at 5:16

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 1
    I don't think anyone can help you unless you share your code. Also you have been focusing on these scripts yet you don't say if you have other extensions installed and interacting with the map document? – Hornbydd Aug 1 at 16:06
  • @Hornbydd Thank you, I've updated the post with code and add-on information. – troubbble Aug 1 at 16:28
  • Having reviewed the code I can't see any gotcha's, no obvious environment settings or odd lines of code. What are the source of the layers, File GeoDatabase or ArcSDE? For a layer to be removed some event is being triggered somewhere. I would start looking at possible extension conflicts. – Hornbydd Aug 1 at 20:49
  • @Hornbydd It looks like it was a Python Addin that others had in their MXDs that was causing the issue. i didn't have the addin so didn't think of it. Thank you for the reminder. I'll come back and delete the question since it was marked as unclear, but wanted to let you know! – troubbble Aug 2 at 13:37