diff --git a/src/formatitko/nop_processor.py b/src/formatitko/nop_processor.py
index bc20a2a..f20ff1b 100644
--- a/src/formatitko/nop_processor.py
+++ b/src/formatitko/nop_processor.py
@@ -127,13 +127,17 @@ class NOPProcessor:
 			raise FormatitkoRecursiveError(e, self.context) from err
 
 	def transform_list(self, e: list[Union[Element, ListContainer]]) -> list[Union[Element, ListContainer]]:
-		for i in range(len(e)):
+		i = 0
+		while i < len(e): # The length of the list can change mid-transformation, so we need to check the length each time
 			e[i] = self.transform(e[i])
+			i-=-1
 		return e
 
 	def transform_ListContainer(self, e: ListContainer) -> ListContainer:
-		for i in range(len(e)):
+		i = 0
+		while i < len(e): # The length of the list can change mid-transformation, so we need to check the length each time
 			e[i] = self.transform(e[i])
+			i-=-1
 		return e
 
 
diff --git a/src/formatitko/output_generator.py b/src/formatitko/output_generator.py
index 332bb25..7ba1278 100644
--- a/src/formatitko/output_generator.py
+++ b/src/formatitko/output_generator.py
@@ -31,7 +31,7 @@ class FormatitkoRecursiveError(Exception):
 	def add_element(self, e: Union[Element, ListContainer, list[Union[Element, ListContainer]]]):
 		self.elements.append(e)
 
-	def pretty_print(self):
+	def pretty_print(self, tracebacklimit: int=0):
 		def eprint(*args, **kwargs):
 			print(*args, file=sys.stderr, **kwargs)
 
@@ -49,7 +49,7 @@ class FormatitkoRecursiveError(Exception):
 			eprint('on line: "' + stringify(line).strip() + '"', end="")
 		eprint()
 		eprint("in element: " + str(self.elements[0]).replace("\n", "\\n"))
-		sys.tracebacklimit = 0
+		sys.tracebacklimit = tracebacklimit
 		raise self.__cause__ from None
 
 
diff --git a/src/formatitko/transform_processor.py b/src/formatitko/transform_processor.py
index d327663..4560e0c 100644
--- a/src/formatitko/transform_processor.py
+++ b/src/formatitko/transform_processor.py
@@ -3,6 +3,7 @@ from panflute import Cite, Code, Emph, Image, LineBreak, Link, Math, Note, Quote
 from panflute import BlockQuote, BulletList, Citation, CodeBlock, Definition, DefinitionItem, DefinitionList, Div, Figure, Header, HorizontalRule, LineBlock, LineItem, ListItem, MetaBlocks, MetaBool, MetaInlines, MetaList, MetaMap, MetaString, Null, OrderedList, Para, Plain, RawBlock, Table, TableBody, TableFoot, TableHead
 from panflute import TableRow, TableCell, Caption, Doc
 from panflute import MetaValue
+from panflute.containers import attach
 from typing import Union, Callable
 from types import ModuleType
 
@@ -75,8 +76,8 @@ class TransformProcessor(NOPProcessor):
 		self.context = Context(e, self.root_file_path)
 		for module, module_name in self._command_modules:
 			self.context.add_commands_from_module(module, module_name)
-		e.content = self.transform(e.content)
 		e.content = [BlockGroup(*e.content, context=self.context)]
+		e.content = self.transform(e.content)
 		return e
 
 
@@ -136,8 +137,9 @@ class TransformProcessor(NOPProcessor):
 			# Commands can be called multiple ways, this handles the following syntax:
 			# :::{c=commandname}
 			# :::
-			e = BlockCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes)
-			return self.transform(e)
+			command = BlockCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes)
+			attach(command, e.parent, e.location, e.index)
+			return self.transform(command)
 
 		if "partial" in e.attributes:
 			# `partial` attribute
@@ -195,14 +197,16 @@ class TransformProcessor(NOPProcessor):
 		if "c" in e.attributes:
 			# Commands can be called multiple ways, this handles the following syntax:
 			# []{c=commandname} and
-			e = InlineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes)
-			return self.transform(e)
+			command = InlineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes)
+			attach(command, e.parent, e.location, e.index)
+			return self.transform(command)
 
 		if len(e.content) == 1 and isinstance(e.content[0], Str):
 			## Handle special command shorthand [!commandname]{}
 			if re.match(r"^![\w.]+$", e.content[0].text):
-				e = InlineCommand(identifier=e.identifier, classes=e.classes, attributes={**e.attributes, "c": e.content[0].text[1:]})
-				return self.transform(e)
+				command = InlineCommand(identifier=e.identifier, classes=e.classes, attributes={**e.attributes, "c": e.content[0].text[1:]})
+				attach(command, e.parent, e.location, e.index)
+				return self.transform(command)
 
 			## Handle import [#ksp_formatitko as ksp]{}, [#ksp_formatitko]{type=module} or [#path/file.md]{type=md}
 			# Import a python module as commands (type=module, the default) or
@@ -255,8 +259,9 @@ class TransformProcessor(NOPProcessor):
 			if not self.context.trusted:
 				return nullify(e)
 			command_output = parse_command(e.text)(BlockCommand(), self.context, self)
-			e = BlockCommand().replaceSelf(*([] if command_output is None else command_output))
-			return self.transform(e)
+			command = BlockCommand().replaceSelf(*([] if command_output is None else command_output))
+			attach(command, e.parent, e.location, e.index)
+			return self.transform(command)
 
 		if "python" in e.classes and ("define" in e.attributes or "redefine" in e.attributes):
 			if not self.context.trusted:
@@ -264,7 +269,9 @@ class TransformProcessor(NOPProcessor):
 			return handle_command_define(e, self.context)
 
 		if "c" in e.attributes:
-			return self.transform(CodeCommand(e.text, identifier=e.identifier, classes=e.classes, attributes=e.attributes))
+			command = CodeCommand(e.text, identifier=e.identifier, classes=e.classes, attributes=e.attributes)
+			attach(command, e.parent, e.location, e.index)
+			return self.transform(command)
 
 		# Pass down metadata 'highlight' and 'highlight_style' as attribute to CodeBlocks
 		# OG now has Context so this is not needed per se, but I'm keeping this here for the handling of attribute > context > default value