Looping through a tree to get branch or leaf
all in all this code does what it needs to do, but czan be improved in some small points

Exception

Instead of the try-except clause, just let the exception bubble up to the caller if he passes a wrong index. returning a string in this case is almost never the correct way to treat it. Now the caller has to check each response he gets and compare it to  "INPUT OUT OF RANGE". Even returning None would be better than this errorstring

list

index = list(index) is useless. tuples can be sliced too, and you can get the length of them

code

The algorithm becomes very simple. It raises a TypeError or IndexError if the caller passes in something illegal, but that's his responsibility

def tree_ref_recursion(tree, index):
    if index:
        return tree_ref_recursion(tree[index[0]], index[1:])
    else:
        return tree


The if index: just checks whether index is a non-empty tuple. There is no need to check for the type of tree. If this is no more tuple, and there is still an index, the caller passed in an illegal tree or index.

recursion

This was a recursive method. Because of the recursion, you need to check the index. You can also use a loop:

def tree_ref(tree, index):
    for idx in index:
        tree = tree[idx]
    return tree