Some options.
Use a persistent communication channel
Instead of HTTP, drop messages in a queue that is highly available and persistent. E.g. Kafka. As long as the target server becomes available at some point, it will get the message.
You have the trade-off of now provisioning and administering a complex subsystem (the queue). So make sure you analyze whether this is worthwhile.
Backoff and retry
Have the caller keep the failed request (possibly persisted to disk) and periodically retry. It's important in this case to distinguish between your request causing a crash vs the service just being down. The former is probably due to a bug and should be logged... retries probably won't make a difference until a fix is made.
Detect and compensate
A periodic task checks for consistency conditions between microservices. E.g. failure logs all the way up to direct API queries as necessary. If it discovers an issue (e.g. there's an order but shipping never received packing list) then do compensation steps. Those steps could be creating a support ticket for a manual fix, or emailing someone, or whatever.
Consider design alternatives
A case like this probably calls for an API gateway to manage calls to affected microservices. That way you control which tactics are used to mitigate this problem. You probably don't want to burden clients with those implementation details. See Circuit-breaker pattern.
Because microservices are independent, there will always exists some failure case that can result in inconsistency. You have to be prepared to do manual fixes when those arise.
If you require strong consistency, then microservices will not be a good fit. If still needing scalability, you might want to look into sharding where related data can be co-located on the same shard for consistency guarantees. You can still scale out IO by adding shards.
If you need strong consistency and aren't having scalability problems, then just use monolithic services. Use libraries as boundaries within your application to separate concerns.