diff --git a/native/chat/inline-multimedia.react.js b/native/chat/inline-multimedia.react.js
--- a/native/chat/inline-multimedia.react.js
+++ b/native/chat/inline-multimedia.react.js
@@ -3,7 +3,7 @@
 import Icon from '@expo/vector-icons/Feather.js';
 import IonIcon from '@expo/vector-icons/Ionicons.js';
 import * as React from 'react';
-import { View, StyleSheet, Text } from 'react-native';
+import { View, Text } from 'react-native';
 import * as Progress from 'react-native-progress';
 import tinycolor from 'tinycolor2';
 
@@ -13,6 +13,7 @@
 import GestureTouchableOpacity from '../components/gesture-touchable-opacity.react.js';
 import type { PendingMultimediaUpload } from '../input/input-state.js';
 import Multimedia from '../media/multimedia.react.js';
+import { useStyles } from '../themes/colors.js';
 
 type Props = {
   +mediaInfo: MediaInfo,
@@ -23,6 +24,7 @@
 };
 function InlineMultimedia(props: Props): React.Node {
   const { mediaInfo, pendingUpload, postInProgress } = props;
+  const styles = useStyles(unboundStyles);
 
   let failed = isLocalUploadID(mediaInfo.id) && !postInProgress;
   let progressPercent = 1;
@@ -35,7 +37,7 @@
   if (failed) {
     progressIndicator = (
       <View style={styles.centerContainer}>
-        <Icon name="alert-circle" style={styles.uploadError} size={64} />
+        <Icon name="alert-circle" style={styles.uploadError} size={42} />
       </View>
     );
   } else if (progressPercent !== 1) {
@@ -100,7 +102,7 @@
   );
 }
 
-const styles = StyleSheet.create({
+const unboundStyles = {
   centerContainer: {
     alignItems: 'center',
     bottom: 0,
@@ -114,14 +116,14 @@
     flex: 1,
   },
   playButton: {
-    color: 'white',
+    color: 'whiteText',
     opacity: 0.9,
     textShadowColor: '#000',
     textShadowOffset: { width: 0, height: 1 },
     textShadowRadius: 1,
   },
   processingStepText: {
-    color: 'white',
+    color: 'whiteText',
     fontSize: 12,
     textShadowColor: '#000',
     textShadowRadius: 1,
@@ -132,18 +134,18 @@
     position: 'absolute',
   },
   progressPercentText: {
-    color: 'white',
+    color: 'whiteText',
     fontSize: 24,
     fontWeight: 'bold',
     textShadowColor: '#000',
     textShadowRadius: 1,
   },
   uploadError: {
-    color: 'white',
-    textShadowColor: '#000',
-    textShadowOffset: { width: 0, height: 1 },
-    textShadowRadius: 1,
+    color: 'whiteText',
+    backgroundColor: 'vibrantRedButton',
+    borderRadius: 21,
+    overflow: 'hidden',
   },
-});
+};
 
 export default InlineMultimedia;
diff --git a/native/media/encrypted-image.react.js b/native/media/encrypted-image.react.js
--- a/native/media/encrypted-image.react.js
+++ b/native/media/encrypted-image.react.js
@@ -73,10 +73,15 @@
       const { result } = await decryptMedia(blobURI, encryptionKey, {
         destination: 'data_uri',
       });
-      // TODO: decide what to do if decryption fails
-      if (result.success && isMounted) {
-        mediaCache?.set(blobURI, result.uri);
-        setSource({ uri: result.uri });
+
+      if (isMounted) {
+        if (result.success) {
+          mediaCache?.set(blobURI, result.uri);
+          setSource({ uri: result.uri });
+        } else {
+          // Setting an invalid uri will cause the Image to run onError
+          setSource({ uri: 'data:,' });
+        }
       }
     };
 
diff --git a/native/media/loadable-image.react.js b/native/media/loadable-image.react.js
--- a/native/media/loadable-image.react.js
+++ b/native/media/loadable-image.react.js
@@ -1,10 +1,12 @@
 // @flow
 
+import Icon from '@expo/vector-icons/Feather.js';
 import { Image } from 'expo-image';
 import * as React from 'react';
-import { View, StyleSheet, ActivityIndicator } from 'react-native';
+import { View, ActivityIndicator } from 'react-native';
 import type { ImageSource } from 'react-native/Libraries/Image/ImageSource';
 
+import { useStyles } from '../themes/colors.js';
 import type { ImageStyle } from '../types/styles.js';
 
 type Props = {
@@ -17,17 +19,24 @@
 };
 function LoadableImage(props: Props): React.Node {
   const { source, placeholder, onLoad: onLoadProp } = props;
+  const styles = useStyles(unboundStyles);
 
   const [loaded, setLoaded] = React.useState(false);
+  const [error, setError] = React.useState(false);
+
+  const onError = React.useCallback(() => {
+    setError(true);
+  }, []);
 
   const onLoad = React.useCallback(() => {
+    setError(false);
     setLoaded(true);
     onLoadProp && onLoadProp();
   }, [onLoadProp]);
 
   const invisibleStyle = React.useMemo(
     () => [props.style, styles.invisible],
-    [props.style],
+    [props.style, styles.invisible],
   );
 
   if (!loaded && props.invisibleLoad) {
@@ -36,41 +45,57 @@
         source={source}
         placeholder={placeholder}
         onLoad={onLoad}
+        onError={onError}
         style={invisibleStyle}
       />
     );
   }
 
-  let spinner;
+  let statusIndicator;
   if (!loaded) {
-    spinner = (
-      <View style={styles.spinnerContainer}>
+    statusIndicator = (
+      <View style={styles.statusIndicatorContainer}>
         <ActivityIndicator color={props.spinnerColor} size="large" />
       </View>
     );
   }
 
+  if (error) {
+    statusIndicator = (
+      <View style={styles.statusIndicatorContainer}>
+        <Icon name="alert-circle" style={styles.errorIndicator} size={42} />
+      </View>
+    );
+  }
+
   return (
     <View style={styles.container}>
       <Image
         source={source}
         placeholder={placeholder}
         onLoad={onLoad}
+        onError={onError}
         style={props.style}
       />
-      {spinner}
+      {statusIndicator}
     </View>
   );
 }
 
-const styles = StyleSheet.create({
+const unboundStyles = {
   container: {
     flex: 1,
   },
+  errorIndicator: {
+    color: 'whiteText',
+    backgroundColor: 'vibrantRedButton',
+    borderRadius: 21,
+    overflow: 'hidden',
+  },
   invisible: {
     opacity: 0,
   },
-  spinnerContainer: {
+  statusIndicatorContainer: {
     alignItems: 'center',
     bottom: 0,
     justifyContent: 'center',
@@ -79,6 +104,6 @@
     right: 0,
     top: 0,
   },
-});
+};
 
 export default LoadableImage;