S3 presigned URLs are being created for items that do not exist. Is this normal behavior? I would rather know if the item is not going to exist when creating the link, than send users to an error page. Obviously, I can check if the item exists before I create the link, but I'm wondering if I'm doing something wrong.
Yes, this is normal. The pre-signed URL is simply a local calculation and signing of a URL. There is no interaction with the S3 service. The S3 object that it refers to does not have to exist.
If you want to ensure that an object exists before you generate a pre-signed URL for it, then you should head
that object.
Note: you can use pre-signed URLs to upload new objects, which obviously don't yet exist at the time you generate the URL. You might also want to use pre-signed URLs to download objects that don't yet exist, but will at some later date (though I admit this is probably not that common a use case).