index out of range for UIList causes panel crash
You simply need to handle the case of an invalid index or an empty list of modifiers.

You could for example test if len(ob.modifiers): ... if there are any, but I would rather catch all bad indices using try ... except IndexError:

import bpy

class OBJECT_PT_modifiers(bpy.types.Panel):
    bl_idname = "OBJECT_PT_modifiers"
    bl_label = "Modifiers"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        layout = self.layout

        rows = 2
        ob = context.object
        idx = ob.modifier_active_index

        try:
            mod = ob.modifiers[idx]
            layout.label("%s (%s)" % (mod.name, mod.type), icon="MODIFIER")
        except IndexError:
            layout.label("no modifier selected")

        row = layout.row()
        row.template_list("UI_UL_list", "modifiers", ob, "modifiers", ob, "modifier_active_index", rows=rows)

def register():
    bpy.utils.register_module(__name__)
    bpy.types.Object.modifier_active_index = bpy.props.IntProperty()

def unregister():
    bpy.utils.unregister_module(__name__)
    del bpy.types.Object.modifier_active_index

if __name__ == "__main__":
    register()


Note that an index of -1 is not considered bad, because it refers to the last item in a list in Python.

You shouldn't use global anywhere, it is certainly not needed. modifier_active_index is globally available for all objects, and you don't need globals inside the draw function to refer to the modifier.

Also note that if you use UI_UL_list, the second argument should be some unique string to prevent collisions with other UI_UL_list that may exist in the panel.

Edit: How to handle (re-)move actions while always showing the panel (even with no modifier)

import bpy

mod_icon_map = {m.identifier: m.icon for m in bpy.types.OBJECT_OT_modifier_add.bl_rna.properties['type'].enum_items}


class OBJECT_OT_modifier_move(bpy.types.Operator): #move active modifier down in stack
    bl_idname = "object.modifier_action"
    bl_label = "Modifier Action"

    action = bpy.props.EnumProperty(
        items=(
            ('UP', "Up", ""),
            ('DOWN', "Down", ""),
            ('REMOVE', "Remove", ""),
        )
    )

    def invoke(self, context, event):

        ob = context.object
        idx = ob.modifier_active_index
        try:
            mod = ob.modifiers[idx]
        except IndexError:
            pass
        else:
            if self.action == 'DOWN' and idx &lt; len(ob.modifiers) - 1:
                if bpy.ops.object.modifier_move_down(modifier=mod.name) == {'FINISHED'}:
                    ob.modifier_active_index += 1
            elif self.action == 'UP' and idx &gt;= 1:
                if bpy.ops.object.modifier_move_up(modifier=mod.name) == {'FINISHED'}:
                    ob.modifier_active_index -= 1                
            elif self.action == 'REMOVE':
                bpy.ops.object.modifier_remove(modifier=mod.name)
                if idx &gt;= 1:
                    ob.modifier_active_index -= 1


        return {"FINISHED"}


class MODIFIER_UL_listtype(bpy.types.UIList): #custom UIList type for modifiers

    def draw_item(self, context, layout, data, item, active_data, active_propname, index):

        modifier = item

        if self.layout_type in {'DEFAULT', 'COMPACT'}:

            layout.prop(modifier, "name", text="", emboss=False, icon=mod_icon_map[modifier.type])

            icon = 'RESTRICT_RENDER_OFF' if item.show_render else 'RESTRICT_RENDER_ON'
            layout.prop(item, "show_render", text="", icon=icon, emboss=False)

            icon = 'RESTRICT_VIEW_OFF' if item.show_viewport else 'RESTRICT_VIEW_ON'
            layout.prop(item, "show_viewport", text="", icon=icon, emboss=False)

            icon = 'EDITMODE_HLT' if item.show_in_editmode else 'OBJECT_DATAMODE'
            layout.prop(item, "show_in_editmode", text="", icon=icon, emboss=False)

        elif self.layout_type in {'GRID'}:
            layout.alignment = 'CENTER'
            layout.label("", icon=mod_icon_map[modifier.type])


class OBJECT_PT_Modifiers(bpy.types.Panel): #panel draw class
    """Creates a Panel in the Object properties window"""
    bl_label = "Modifiers"
    bl_idname = "OBJECT_PT_Modifiers"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"


    def draw(self, context):
        layout = self.layout

        ob = bpy.context.object
        rows = 2

        row = layout.row()
        row.template_list("MODIFIER_UL_listtype", "", ob, "modifiers", ob, "modifier_active_index", rows=rows)
        col = row.column(align=True)
        col.operator("object.modifier_add", icon='ZOOMIN', text="")
        col.operator("object.modifier_action", icon='ZOOMOUT', text="").action = 'REMOVE'
        col.separator()
        col.operator("object.modifier_action", icon='TRIA_UP', text="").action = 'UP'
        col.operator("object.modifier_action", icon='TRIA_DOWN', text="").action = 'DOWN'


def register():
    bpy.utils.register_module(__name__)
    bpy.types.Object.modifier_active_index = bpy.props.IntProperty()


def unregister():
    bpy.utils.unregister_module(__name__)
    del bpy.types.Object.modifier_active_index


if __name__ == "__main__":
    register()


Remarks:


Creating a lookup dict is way better for icons
Move up, move down and remove actions can be handled in a single operator
Don't do ob.modifiers[ob.modifier_active_index] anywhere without range change or try/except construct
Check the result of modifier_move_up() and modifier_move_down(), as it may fail (MultiRes can't be below a non-deforming modifier)
All classes can be (un-)registered at once with (un-)register_module()
Use consistent indentation (4 spaces for every level)