     ValInsertvalueinst -> translateInsertValueInst finalState ii
   return $ ConstantValue vt
 
-translateMetadata :: MetaPtr -> KnotMonad Metadata
-translateMetadata mp = do
+translateMetadata :: KnotState -> MetaPtr -> KnotMonad Metadata
+translateMetadata finalState mp = do
   s <- get
   let ip = ptrToIntPtr mp
+  put s { visitedMetadata = S.insert ip (visitedMetadata s) }
   case M.lookup ip (metaMap s) of
     Just m -> return m
-    Nothing -> translateMetadata' mp
-
-maybeTranslateMetadata :: Maybe MetaPtr -> KnotMonad (Maybe Metadata)
-maybeTranslateMetadata Nothing = return Nothing
-maybeTranslateMetadata (Just mp) = Just <$> translateMetadata mp
-
-translateMetadata' :: MetaPtr -> KnotMonad Metadata
-translateMetadata' mp = do
+    Nothing -> translateMetadata' finalState mp
+
+translateMetadataRec :: KnotState -> MetaPtr -> KnotMonad Metadata
+translateMetadataRec finalState mp = do
+  s <- get
   let ip = ptrToIntPtr mp
+  -- If we have already visited this metadata object, look it up in
+  -- the final state.  We record visits *before* making recursive
+  -- calls, allowing us to tie the knot by looking already-visited
+  -- nodes up in the final state.
+  --
+  -- If we haven't seen this node before, we can safely call the
+  -- outermost 'translateMetadata', which will make an entry in the
+  -- visited set and then do the translation.
+  case S.member ip (visitedMetadata s) of
+    False -> translateMetadata finalState mp
+    True -> return $ M.findWithDefault (throw MetaKnotFailure) ip (metaMap finalState)
+
+maybeTranslateMetadataRec :: KnotState -> Maybe MetaPtr -> KnotMonad (Maybe Metadata)
+maybeTranslateMetadataRec _ Nothing = return Nothing
+maybeTranslateMetadataRec finalState (Just mp) =
+  Just <$> translateMetadataRec finalState mp
+
+translateMetadata' :: KnotState -> MetaPtr -> KnotMonad Metadata
+translateMetadata' finalState mp = do
+  let ip = ptrToIntPtr mp
+  s <- get
+  put s { visitedMetadata = S.insert ip (visitedMetadata s) }
   metaTag <- liftIO $ cMetaTypeTag mp
   tag <- liftIO $ cMetaTag mp
   content <- case metaTag of

      line <- liftIO $ cMetaTemplateValueLine mp
      col <- liftIO $ cMetaTemplateValueColumn mp

      ctxt' <- translateMetadata ctxt
      ty' <- translateMetadata ty

      return MetaDWTemplateValueParameter { metaTemplateValueParameterContext = ctxt'
                                          , metaTemplateValueParameterType = ty'

      col <- liftIO $ cMetaLocationColumn mp
      scope <- liftIO $ cMetaLocationScope mp

      scope' <- translateMetadataRec finalState scope
      return MetaSourceLocation { metaSourceRow = line
                                , metaSourceCol = col
                                , metaSourceScope = scope'
