diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## [1.2.3](https://github.com/haskell-github-trust/effectful-config-file/compare/1.2.2...1.2.3) (2026-11-11)
+
+### Bug Fixes
+
+* avoid error span on expected not found config ([7161128](https://github.com/haskell-github-trust/effectful-config-file/commit/716112893565592f9f42d749b76db18dcaa31071))
+
 ## [1.2.2](https://github.com/haskell-github-trust/effectful-config-file/compare/1.2.1...1.2.2) (2026-10-31)
 
 ## [1.2.1](https://github.com/haskell-github-trust/effectful-config-file/compare/1.2.0...1.2.1) (2026-10-31)
diff --git a/effectful-config-file.cabal b/effectful-config-file.cabal
--- a/effectful-config-file.cabal
+++ b/effectful-config-file.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           effectful-config-file
-version:        0.1.2.2
+version:        0.1.2.3
 synopsis:       Config-file effect for effectful
 description:    Config-file parser and utilities to use in conjunction with effectful
 category:       Configuration
diff --git a/src/Data/Config.hs b/src/Data/Config.hs
--- a/src/Data/Config.hs
+++ b/src/Data/Config.hs
@@ -17,7 +17,8 @@
 ) where
 
 import Control.Applicative.Combinators (choice)
-import Control.Monad (void)
+import Control.Exception (IOException, tryJust)
+import Control.Monad (guard, void, (<=<))
 import Data.ByteString.Char8 (ByteString)
 import qualified Data.ByteString.Char8 as B (pack)
 import Data.List (isPrefixOf)
@@ -41,6 +42,7 @@
 import System.Directory (getHomeDirectory)
 import System.Environment (getEnv)
 import System.FilePath ((</>))
+import System.IO.Error (isDoesNotExistError)
 import Text.Megaparsec (
   Parsec,
   chunk,
@@ -162,24 +164,42 @@
     addAttribute sp "config.key" key
     act sp
 
+-- | A missing env var or file is an expected miss (callers fall back), so it
+-- is tagged @config.found=false@ instead of failing the span, and rethrown
+-- by 'rethrowMissing' once the span has ended.
+tryMissing
+  :: IOE :> es
+  => Span
+  -> IO b
+  -> Eff es (Either IOException b)
+tryMissing sp io = do
+  r <- liftIO $ tryJust (\e -> e <$ guard (isDoesNotExistError e)) io
+  addAttribute sp "config.found" (either (const False) (const True) r)
+  pure r
+
+rethrowMissing :: IOE :> es => Either IOException b -> Eff es b
+rethrowMissing = either (liftIO . ioError) pure
+
 instance ConfigParse a => ConfigFetch (ConfigValue a) where
   fetch (Env e) =
-    withConfigSpan "env" (T.pack e) $ \_ -> do
-      logDebug $ "fetching config value from env " <> T.pack e
-      v <- liftIO (getEnv e)
-      pure $ Literal (parse (T.pack v))
+    fmap (Literal . parse . T.pack) . rethrowMissing
+      <=< withConfigSpan "env" (T.pack e)
+      $ \sp -> do
+        logDebug $ "fetching config value from env " <> T.pack e
+        tryMissing sp (getEnv e)
   fetch (File fp) =
-    withConfigSpan "file" (T.pack fp) $ \_ -> do
-      fp' <-
-        if "~/" `isPrefixOf` fp
-          then do
-            home <- liftIO getHomeDirectory
-            pure $ home </> drop 2 fp
-          else pure fp
+    fmap (Literal . parse . T.pack) . rethrowMissing
+      <=< withConfigSpan "file" (T.pack fp)
+      $ \sp -> do
+        fp' <-
+          if "~/" `isPrefixOf` fp
+            then do
+              home <- liftIO getHomeDirectory
+              pure $ home </> drop 2 fp
+            else pure fp
 
-      logDebug $ "fetching config value from file " <> T.pack fp
-      v <- liftIO (readFile fp')
-      pure $ Literal (parse (T.pack v))
+        logDebug $ "fetching config value from file " <> T.pack fp
+        tryMissing sp (readFile fp')
   fetch (Secret k) =
     withConfigSpan "secretspec" k $ \_ -> do
       logDebug $ "fetching config value from secretspec " <> k
