diff --git a/pandoc-lua-engine.cabal b/pandoc-lua-engine.cabal
--- a/pandoc-lua-engine.cabal
+++ b/pandoc-lua-engine.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.4
 name:                pandoc-lua-engine
-version:             0.2.1.2
+version:             0.2.1.3
 build-type:          Simple
 license:             GPL-2.0-or-later
 license-file:        COPYING.md
@@ -101,7 +101,7 @@
                      , aeson
                      , bytestring            >= 0.9     && < 0.13
                      , citeproc              >= 0.8     && < 0.9
-                     , containers            >= 0.6.0.1 && < 0.7
+                     , containers            >= 0.6.0.1 && < 0.8
                      , data-default          >= 0.4     && < 0.8
                      , doclayout             >= 0.4     && < 0.5
                      , doctemplates          >= 0.11    && < 0.12
@@ -114,9 +114,9 @@
                      , hslua-module-version  >= 1.1     && < 1.2
                      , hslua-module-zip      >= 1.1     && < 1.2
                      , hslua-repl            >= 0.1.1   && < 0.2
-                     , lpeg                  >= 1.0.4   && < 1.1
+                     , lpeg                  >= 1.1     && < 1.2
                      , mtl                   >= 2.2     && < 2.4
-                     , pandoc                >= 3.1.2   && < 3.2
+                     , pandoc                >= 3.1.4   && < 3.2
                      , pandoc-lua-marshal    >= 0.2.2   && < 0.3
                      , pandoc-types          >= 1.22    && < 1.24
                      , parsec                >= 3.1     && < 3.2
diff --git a/src/Text/Pandoc/Lua/Custom.hs b/src/Text/Pandoc/Lua/Custom.hs
--- a/src/Text/Pandoc/Lua/Custom.hs
+++ b/src/Text/Pandoc/Lua/Custom.hs
@@ -22,11 +22,13 @@
 import Text.Pandoc.Lua.Marshal.Format (peekExtensionsConfig)
 import Text.Pandoc.Lua.Marshal.Pandoc (peekPandoc)
 import Text.Pandoc.Lua.Marshal.WriterOptions (pushWriterOptions)
+import Text.Pandoc.Lua.PandocLua (unPandocLua)
 import Text.Pandoc.Readers (Reader (..))
 import Text.Pandoc.Sources (ToSources(..))
 import Text.Pandoc.Scripting (CustomComponents (..))
 import Text.Pandoc.Writers (Writer (..))
 import qualified Text.Pandoc.Lua.Writer.Classic as Classic
+import qualified Text.Pandoc.Class as PandocMonad
 
 -- | Convert custom markup to Pandoc.
 loadCustom :: (PandocMonad m, MonadIO m)
@@ -80,15 +82,21 @@
           pure $
             if docType /= TypeFunction
             then Nothing
-            else Just . TextWriter $ \opts doc ->
+            else Just . TextWriter $ \opts doc -> do
+              -- See TextWriter below for why the state is updated
+              st <- PandocMonad.getCommonState
               liftIO $ withGCManagedState luaState $
-              Classic.runCustom @PandocError opts doc
+                unPandocLua (PandocMonad.putCommonState st) >>
+                Classic.runCustom @PandocError opts doc
         _ -> Just <$!> do
           -- Binary writer. Writer function is on top of the stack.
           setfield registryindex writerField
-          pure $ ByteStringWriter $ \opts doc ->
+          pure $ ByteStringWriter $ \opts doc -> do
+            -- See TextWriter below for why the state is updated
+            st <- PandocMonad.getCommonState
             -- Call writer with document and writer options as arguments.
             liftIO $ withGCManagedState luaState $ do
+              unPandocLua (PandocMonad.putCommonState st)
               getfield registryindex writerField
               push doc
               pushWriterOptions opts
@@ -97,8 +105,13 @@
       _ -> Just <$!> do
         -- New-type text writer. Writer function is on top of the stack.
         setfield registryindex writerField
-        pure $ TextWriter $ \opts doc ->
+        pure $ TextWriter $ \opts doc -> do
+          -- The CommonState might have changed since the Lua file was
+          -- loaded. That's why the state must be updated when the
+          -- writer is run. (#9229)
+          st <- PandocMonad.getCommonState
           liftIO $ withGCManagedState luaState $ do
+            unPandocLua (PandocMonad.putCommonState st)
             getfield registryindex writerField
             push doc
             pushWriterOptions opts
diff --git a/src/Text/Pandoc/Lua/Module/Pandoc.hs b/src/Text/Pandoc/Lua/Module/Pandoc.hs
--- a/src/Text/Pandoc/Lua/Module/Pandoc.hs
+++ b/src/Text/Pandoc/Lua/Module/Pandoc.hs
@@ -20,11 +20,12 @@
 import Prelude hiding (read)
 import Control.Applicative ((<|>))
 import Control.Monad (forM_, when)
-import Control.Monad.Catch (catch, throwM)
+import Control.Monad.Catch (catch, handle, throwM)
 import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr)
 import Data.Default (Default (..))
 import Data.Maybe (fromMaybe)
 import Data.Proxy (Proxy (Proxy))
+import Data.Text.Encoding.Error (UnicodeException)
 import HsLua
 import System.Exit (ExitCode (..))
 import Text.Pandoc.Definition
@@ -210,7 +211,8 @@
     =?> "output string, or error triple"
 
   , defun "read"
-    ### (\content mformatspec mreaderOptions -> unPandocLua $ do
+    ### (\content mformatspec mreaderOptions ->
+          handle (failLua . show @UnicodeException) . unPandocLua $ do
             flvrd <- maybe (parseFlavoredFormat "markdown") pure mformatspec
             let readerOpts = fromMaybe def mreaderOptions
             getReader flvrd >>= \case
diff --git a/test/lua/module/pandoc-structure.lua b/test/lua/module/pandoc-structure.lua
--- a/test/lua/module/pandoc-structure.lua
+++ b/test/lua/module/pandoc-structure.lua
@@ -48,7 +48,7 @@
       assert.are_equal('Div', hblks[1].t)
       assert.are_equal('Header', hblks[1].content[1].t)
       assert.are_equal('1',   hblks[1].content[1].attributes['number'])
-      assert.are_equal('1.1', hblks[1].content[3].attributes['number'])
+      assert.are_equal('1.0.1', hblks[1].content[3].attributes['number'])
     end)
   },
 
@@ -110,10 +110,10 @@
       assert.are_equal(
         structure.table_of_contents(chunked),
         pandoc.BulletList{
-          {pandoc.Plain({pandoc.Link('First', 'chunk-001', '', {id='toc-first'})}),
-           pandoc.BulletList{{pandoc.Plain({pandoc.Link('Subsection', 'chunk-002', '', {id='toc-subsection'})})}}
+          {pandoc.Plain({pandoc.Link('First', 'chunk-001#first', '', {id='toc-first'})}),
+           pandoc.BulletList{{pandoc.Plain({pandoc.Link('Subsection', 'chunk-002#subsection', '', {id='toc-subsection'})})}}
           },
-          {pandoc.Plain({pandoc.Link('Second', 'chunk-003', '', {id='toc-second'})})}
+          {pandoc.Plain({pandoc.Link('Second', 'chunk-003#second', '', {id='toc-second'})})}
         }
       )
     end),
@@ -130,8 +130,8 @@
       assert.are_equal(
         structure.table_of_contents(chunked, {toc_depth = 1}),
         pandoc.BulletList{
-          {pandoc.Plain({pandoc.Link('First', 'chunk-001', '', {id='toc-first'})})},
-          {pandoc.Plain({pandoc.Link('Second', 'chunk-002', '', {id='toc-second'})})}
+          {pandoc.Plain({pandoc.Link('First', 'chunk-001#first', '', {id='toc-first'})})},
+          {pandoc.Plain({pandoc.Link('Second', 'chunk-002#second', '', {id='toc-second'})})}
         }
       )
     end),
