diff --git a/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java b/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java
index db07ed5d4d6a8188fd687f8ff55f33dbbba5f76a..cf79c881a0a384e37a78eaa1216b250a86619e5a 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java
@@ -119,9 +119,9 @@ public abstract class AbstractFile extends AbstractContent {
 	private volatile String uniquePath;
 	private volatile FileSystem parentFileSystem;
 	
-	private boolean tryContentStream;
-	private Object contentStreamLock = new Object();
-	private SoftReference<ContentStream> contentStreamRef = null;
+	private final boolean tryContentProviderStream;
+	private Object contentProviderStreamLock = new Object();
+	private SoftReference<ContentProviderStream> contentProviderStreamRef = null;
 
 	/**
 	 * Initializes common fields used by AbstactFile implementations (objects in
@@ -234,8 +234,8 @@ public abstract class AbstractFile extends AbstractContent {
 		this.osAccountObjId = osAccountObjectId;
 		this.collected = collected;
 		// any item that is marked as YES_REPO and there is a custom content provider for the db will attempt to use the content provider to provide data
-		// this will be flipped to false if there is no content stream from the content provider for this file
-		this.tryContentStream = collected == CollectedStatus.YES_REPO && db.getContentProvider() != null;
+		// this will be flipped to false if there is no content provider stream from the content provider for this file
+		this.tryContentProviderStream = collected == CollectedStatus.YES_REPO && db.getContentProvider() != null;
 		if (Objects.nonNull(fileAttributes) && !fileAttributes.isEmpty()) {
 			this.fileAttributesCache.addAll(fileAttributes);
 			loadedAttributesCacheFromDb = true;
@@ -1072,58 +1072,47 @@ short getMetaFlagsAsInt() {
 		return TSK_FS_META_FLAG_ENUM.toInt(metaFlags);
 	}
 	
-
-	
-	private boolean hasContentStream() {
-		ContentStream contentStream = null;
-		try {
-			contentStream = getContentStream();	
-		} catch (TskCoreException ex) {
-			LOGGER.log(Level.WARNING, "An error occurred while loading content stream for file with id: " + getId(), ex);
-		}
-		return contentStream != null;
-	}
-	
 	/**
-	 * Attempts to load the content stream for this file.  If none exists, returns null.
-	 * @return  The content stream for this file or null if none exists.
-	 * @throws TskCoreException 
+	 * Attempts to get cached or load the content provider stream for this file.
+	 * If none exists, returns null.
+	 *
+	 * NOTE: Does not check the value for tryContentProviderStream before
+	 * attempting.
+	 *
+	 * @return The content stream for this file or null if none exists.
+	 *
+	 * @throws TskCoreException
 	 */
-	private ContentStream getContentStream() throws TskCoreException {
-		ContentStream contentStream = null;
-		if (tryContentStream) {
-			try {
-				synchronized (contentStreamLock) {
-					// try to get soft reference content stream
-					contentStream = contentStreamRef == null ? null : contentStreamRef.get();
-					// load if not cached and then cache if present
-					if (contentStream == null) {
-						contentStream = getSleuthkitCase().getContentProvider().getContentStream(this).orElse(null);
-						if (contentStream != null) {
-							this.contentStreamRef = new SoftReference<>(contentStream);
-						}
-					}
+	private ContentProviderStream getContentProviderStream() throws TskCoreException {
+		synchronized (contentProviderStreamLock) {
+			// try to get soft reference content provider stream
+			ContentProviderStream contentProviderStream = contentProviderStreamRef == null ? null : contentProviderStreamRef.get();
+			// load if not cached and then cache if present
+			if (contentProviderStream == null) {
+				ContentStreamProvider provider = getSleuthkitCase().getContentProvider();
+				contentProviderStream = provider == null ? null : provider.getContentStream(this).orElse(null);
+
+				if (contentProviderStream == null) {
+					throw new TskCoreException(MessageFormat.format("Could not get content provider string for file with obj id: {0}, path: {1}",
+							getId(),
+							getUniquePath()));
 				}
-			} finally {
-				if (contentStream == null) {
-					// don't try to load the content stream again if it fails to load (either through exception or not existing)
-					tryContentStream = false;
-				}				
+
+				this.contentProviderStreamRef = new SoftReference<>(contentProviderStream);
 			}
+
+			return contentProviderStream;
 		}
-		return contentStream;
 	}
 	
 	@Override
 	public final int read(byte[] buf, long offset, long len) throws TskCoreException {
-		// try to use content stream if present
-		ContentStream contentStream = getContentStream();
-		if (contentStream != null) {
-			return contentStream.read(buf, offset, len);
-		}
-		
-		//if localPath is set, use local, otherwise, use readCustom() supplied by derived class
-		if (localPathSet) {
+		// try to use content provider stream if should use
+		if (tryContentProviderStream) {
+			ContentProviderStream contentProviderStream = getContentProviderStream();
+			return contentProviderStream.read(buf, offset, len);
+		} else if (localPathSet) {
+			//if localPath is set, use local, otherwise, use readCustom() supplied by derived class
 			return readLocal(buf, offset, len);
 		} else {
 			return readInt(buf, offset, len);
@@ -1289,17 +1278,14 @@ final void setEncodingType(TskData.EncodingType encodingType) {
 	}
 
 	/**
-	 * Check if the file exists. If non-local always true, if local, checks if
-	 * actual local path exists
+	 * Check if the file exists. If non-local or file is marked with YES_REPO
+	 * and there is a content provider always true, if local, checks if actual
+	 * local path exists
 	 *
 	 * @return true if the file exists, false otherwise
 	 */
 	public boolean exists() {
-		if (hasContentStream()) {
-			return true;
-		}
-		
-		if (!localPathSet) {
+		if (tryContentProviderStream || !localPathSet) {
 			return true;
 		} else {
 			try {
@@ -1314,17 +1300,13 @@ public boolean exists() {
 
 	/**
 	 * Check if the file exists and is readable. If non-local (e.g. within an
-	 * image), always true, if local, checks if actual local path exists and is
-	 * readable
+	 * image) or file is marked with YES_REPO and there is a content provider,
+	 * always true, if local, checks if actual local path exists and is readable
 	 *
 	 * @return true if the file is readable
 	 */
 	public boolean canRead() {
-		if (hasContentStream()) {
-			return true;
-		}
-		
-		if (!localPathSet) {
+		if (tryContentProviderStream || !localPathSet) {
 			return true;
 		} else {
 			try {
diff --git a/bindings/java/src/org/sleuthkit/datamodel/ContentStream.java b/bindings/java/src/org/sleuthkit/datamodel/ContentProviderStream.java
similarity index 71%
rename from bindings/java/src/org/sleuthkit/datamodel/ContentStream.java
rename to bindings/java/src/org/sleuthkit/datamodel/ContentProviderStream.java
index 00055d0a78188f1dada7745736c29747fdeecccb..5617362036bb1a9f2c55991bd7d39072c5e44552 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/ContentStream.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/ContentProviderStream.java
@@ -18,12 +18,11 @@
  */
 package org.sleuthkit.datamodel;
 
-import java.util.Optional;
-
 /**
  * Custom provider for content bytes.
  */
-public interface ContentStream extends AutoCloseable {
+@SuppressWarnings("try")
+public interface ContentProviderStream extends AutoCloseable {
 
 	/**
 	 * Reads data that this content object is associated with (file contents,
@@ -39,21 +38,4 @@ public interface ContentStream extends AutoCloseable {
 	 *                          tsk core
 	 */
 	public int read(byte[] buf, long offset, long len) throws TskCoreException;
-
-	/**
-	 * Custom provider for bytes of an abstract file.
-	 */
-	public interface ContentProvider {
-
-		/**
-		 * Provides a content stream for a content object or empty if this
-		 * provider has none to provide.
-		 *
-		 * @param content The content.
-		 *
-		 * @return The content stream or empty if no stream can be provided
-		 *         for this content.
-		 */
-		Optional<ContentStream> getContentStream(Content content) throws TskCoreException;
-	}
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/ContentStreamProvider.java b/bindings/java/src/org/sleuthkit/datamodel/ContentStreamProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..c7b09bd2bd46e803ee7d64511d871569bd65efe8
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/ContentStreamProvider.java
@@ -0,0 +1,38 @@
+/*
+ * SleuthKit Java Bindings
+ *
+ * Copyright 2023 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.datamodel;
+
+import java.util.Optional;
+
+/**
+ * Custom provider for bytes of an abstract file.
+ */
+public interface ContentStreamProvider {
+
+	/**
+	 * Provides a content stream for a content object or empty if this provider
+	 * has none to provide.
+	 *
+	 * @param content The content.
+	 *
+	 * @return The content stream or empty if no stream can be provided for this
+	 *         content.
+	 */
+	Optional<ContentProviderStream> getContentStream(Content content) throws TskCoreException;
+}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
index 5f6f482dca0caa05c452974e2ba691879ebdc6bd..57b86d06429adf024d0f423524c997ddb2771216 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java
@@ -94,7 +94,6 @@
 import org.sqlite.SQLiteConfig;
 import org.sqlite.SQLiteDataSource;
 import org.sqlite.SQLiteJDBCLoader;
-import org.sleuthkit.datamodel.ContentStream.ContentProvider;
 
 /**
  * Represents the case database with methods that provide abstractions for
@@ -205,7 +204,7 @@ public class SleuthkitCase {
 	private final Cache<Long, Boolean> isRootDirectoryCache
 			= CacheBuilder.newBuilder().maximumSize(200000).expireAfterAccess(5, TimeUnit.MINUTES).build();
 	// custom provider for file bytes (can be null)
-	private final ContentProvider contentProvider;
+	private final ContentStreamProvider contentProvider;
 	
 	/*
 	 * First parameter is used to specify the SparseBitSet to use, as object IDs
@@ -339,7 +338,7 @@ public static void tryConnect(CaseDbConnectionInfo info) throws TskCoreException
 	 *
 	 * @throws Exception
 	 */
-	private SleuthkitCase(String dbPath, SleuthkitJNI.CaseDbHandle caseHandle, DbType dbType, ContentProvider contentProvider) throws Exception {
+	private SleuthkitCase(String dbPath, SleuthkitJNI.CaseDbHandle caseHandle, DbType dbType, ContentStreamProvider contentProvider) throws Exception {
 		Class.forName("org.sqlite.JDBC");
 		this.dbPath = dbPath;
 		this.dbType = dbType;
@@ -372,7 +371,7 @@ private SleuthkitCase(String dbPath, SleuthkitJNI.CaseDbHandle caseHandle, DbTyp
 	 *
 	 * @throws Exception
 	 */
-	private SleuthkitCase(String host, int port, String dbName, String userName, String password, SleuthkitJNI.CaseDbHandle caseHandle, String caseDirPath, DbType dbType, ContentProvider contentProvider) throws Exception {
+	private SleuthkitCase(String host, int port, String dbName, String userName, String password, SleuthkitJNI.CaseDbHandle caseHandle, String caseDirPath, DbType dbType, ContentStreamProvider contentProvider) throws Exception {
 		this.dbPath = "";
 		this.databaseName = dbName;
 		this.dbType = dbType;
@@ -421,7 +420,7 @@ private void init() throws Exception {
 	 * @return The custom content provider for this case if one exists.
 	 *         Otherwise, returns null.
 	 */
-	ContentProvider getContentProvider() {
+	ContentStreamProvider getContentProvider() {
 		return this.contentProvider;
 	}
 
@@ -3002,7 +3001,7 @@ public static SleuthkitCase openCase(String dbPath) throws TskCoreException {
 	 * @throws org.sleuthkit.datamodel.TskCoreException
 	 */
 	@Beta
-	public static SleuthkitCase openCase(String dbPath, ContentProvider provider) throws TskCoreException {
+	public static SleuthkitCase openCase(String dbPath, ContentStreamProvider provider) throws TskCoreException {
 		try {
 			final SleuthkitJNI.CaseDbHandle caseHandle = SleuthkitJNI.openCaseDb(dbPath);
 			return new SleuthkitCase(dbPath, caseHandle, DbType.SQLITE, provider);
@@ -3042,7 +3041,7 @@ public static SleuthkitCase openCase(String databaseName, CaseDbConnectionInfo i
 	 * @throws TskCoreException If there is a problem opening the database.
 	 */
 	@Beta
-	public static SleuthkitCase openCase(String databaseName, CaseDbConnectionInfo info, String caseDir, ContentProvider contentProvider) throws TskCoreException {
+	public static SleuthkitCase openCase(String databaseName, CaseDbConnectionInfo info, String caseDir, ContentStreamProvider contentProvider) throws TskCoreException {
 		try {
 			/*
 			 * The flow of this method involves trying to open case and if
@@ -3094,7 +3093,7 @@ public static SleuthkitCase newCase(String dbPath) throws TskCoreException {
 	 * @throws org.sleuthkit.datamodel.TskCoreException
 	 */
 	@Beta
-	public static SleuthkitCase newCase(String dbPath, ContentProvider contentProvider) throws TskCoreException {
+	public static SleuthkitCase newCase(String dbPath, ContentStreamProvider contentProvider) throws TskCoreException {
 		try {
 			CaseDatabaseFactory factory = new CaseDatabaseFactory(dbPath);
 			factory.createCaseDatabase();
@@ -3143,7 +3142,7 @@ public static SleuthkitCase newCase(String caseName, CaseDbConnectionInfo info,
 	 * @throws org.sleuthkit.datamodel.TskCoreException
 	 */
 	@Beta
-	public static SleuthkitCase newCase(String caseName, CaseDbConnectionInfo info, String caseDirPath, ContentProvider contentProvider) throws TskCoreException {
+	public static SleuthkitCase newCase(String caseName, CaseDbConnectionInfo info, String caseDirPath, ContentStreamProvider contentProvider) throws TskCoreException {
 		String databaseName = createCaseDataBaseName(caseName);
 		try {
 			/**