Here is the instructions given:

Use a loop with indirect or indexed addressing to reverse the elements of an integer array in place. Do
not copy the elements to any other array. Use the SIZEOF, TYPE, and LENGTHOF operators to make
the program as flexible as possible if the array size and type should be changed in the future. Optionally,
you may display the modified array by calling the DumpMem method from the Irvine32 library.

Here is my code; I am experiencing an infinite loop then an error for some reason

array BYTE 10h, 20h, 30h, 40h

main PROC
    mov esi, 0
    mov edi, 0
    mov esi, OFFSET array + SIZEOF array - 1
    mov edi, OFFSET array + SIZEOF array - 1
    mov ecx, SIZEOF array/2

l1: mov al, [esi]
    mov bl, [edi]
    mov [edi], al
    mov [esi], bl
    inc esi
    dec edi
    LOOP l1

    call DumpRegs
    call DumpMem


main ENDP

END main

The problem is that you initialize ESI to the end of the array. ESI should be set to the beginning of the array.

ESI is a general purpose register, it does not care if it is set to the
end of an array, in fact that is one of it's intended purposes:

Example code, Set DirectionFlag to 1 to go from higher to lower address
using MOVSW, ESI will be decremented by 2 each time

STD ; goto lower addresses
MOV ESI, OFFSET array1_end - 2
MOV EDI, OFFSET array2_end - 2
MOV ECX, 16 ; move 16
REP MOVSW ; copy 16 words
array1 DW 16 DUP(?)
array1_end equ $
array2 DW 16 DUP(?)
array2_end equ $

This will copy the contents of the array at DS:ESI into the
array at ES:EDI, starting from the end to the beginning.