`self._context` wiped?
- I have read the Filing Issues and subsequent “How to Get Help” sections of the documentation.
- [-] I can reproduce this problem with stock/default settings file, theme, and sample content (as described in above “How to Get Help” sections of the documentation).
- I have searched the issues (including closed ones) and believe that this is not a duplicate.
I'm working on creating a plugin to read "task notes" -- basically a markdown file that contains a bunch of metadata about a "task" -- and then render them as special posts.
Internally, this reader is using the model given in the docs of how to inject content.
Everything works fine until I try get to rendering the site. If there is a link from one tasknote to another, Pelican blows up with a KeyError while it tries to resolve the internal link. The cause of this seems to be that self_.context is wiped to an empty directory: {}. But don't understand how this would happen, as I don't play with the context after the Generators are run.
Am I missing some piece, something that the Generators need to do?
I added some logging to Pelican, and this is the context (specially, generated_content which is used to resolve links) after the Generator is done running:
self.context["generated_content"] =
{'Blog/20210919-covid-election.md': <pelican.contents.Article object at 0x0000024342492390>,
'Blog/posted/20230312 minchin.jrnl.md': <pelican.contents.Article object at 0x00000243420DC890>,
'GTD/projects/Personal Upkeep.md': <pelican.contents.Article object at 0x00000243420F6390>,
'GTD/tasks/2025/task-202508251118.md': <pelican.contents.Article object at 0x000002434202BE30>,
'GTD/tasks/2025/task-202509101610.md': <pelican.contents.Article object at 0x00000243424BADB0>,
'GTD/tasks/2025/task-202509112051.md': <pelican.contents.Article object at 0x000002434202B9B0>,
'GTD/tasks/2025/task-202509112052.md': <pelican.contents.Article object at 0x00000243420F7320>,
'V/2016/12/19.md': <pelican.contents.Article object at 0x000002434202A210>,
'checkbox-test.md': <pelican.contents.Article object at 0x0000024342059A60>,
'journal/2014/02/13.md': <pelican.contents.Article object at 0x000002434210B890>,
'journal/2019/04/06.md': <pelican.contents.Article object at 0x0000024342492B40>,
'reader-issue-2.md': <pelican.contents.Article object at 0x000002434210B830>,
'test.md': <pelican.contents.Article object at 0x000002434202B3E0>,
'zettel/2013/201307151200.md': <pelican.contents.Article object at 0x0000024342493170>,
'zettel/2023/202307042224.md': <pelican.contents.Article object at 0x00000243420F6750>,
'zettel/2023/202311222034.md': <pelican.contents.Article object at 0x00000243420DDD90>}Near as I can tell, this looks like it should.
This, however, is what the Jinja template engine is being provided:
self=<pelican.contents.Article object at 0x00000243424BADB0>
key='generated_content'
path='GTD/tasks/2025/task-202508251118.md'
self._context=
{}
self.title='setup accounts for the family'It is trying to resolve an internal link to GTD/tasks/2025/task-202508251118.md.
This is the stack trace it provides (using pelican --debug):
CRITICAL KeyError: 'generated_content' __init__.py:685
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ D:\Code\pelican\pelican\__init__.py:681 in main │
│ │
│ 678 │ │ │ ) │
│ 679 │ │ else: │
│ 680 │ │ │ with console.status("Generating..."): │
│ ❱ 681 │ │ │ │ pelican.run() │
│ 682 │ except KeyboardInterrupt: │
│ 683 │ │ logger.warning("Keyboard interrupt received. Exiting.") │
│ 684 │ except Exception as e: │
│ │
│ D:\Code\pelican\pelican\__init__.py:145 in run │
│ │
│ 142 │ │ │
│ 143 │ │ for p in generators: │
│ 144 │ │ │ if hasattr(p, "generate_output"): │
│ ❱ 145 │ │ │ │ p.generate_output(writer) │
│ 146 │ │ │
│ 147 │ │ logger.debug("Signal finalized.send()") │
│ 148 │ │ signals.finalized.send(self) │
│ │
│ D:\Code\pelican\pelican\generators.py:890 in generate_output │
│ │
│ 887 │ │
│ 888 │ def generate_output(self, writer): │
│ 889 │ │ self.generate_feeds(writer) │
│ ❱ 890 │ │ self.generate_pages(writer) │
│ 891 │ │ signals.article_writer_finalized.send(self, writer=writer) │
│ 892 │ │
│ 893 │ def refresh_metadata_intersite_links(self): │
│ │
│ D:\Code\pelican\pelican\generators.py:692 in generate_pages │
│ │
│ 689 │ │ │
│ 690 │ │ # to minimize the number of relative path stuff modification │
│ 691 │ │ # in writer, articles pass first │
│ ❱ 692 │ │ self.generate_articles(write) │
│ 693 │ │ self.generate_period_archives(write) │
│ 694 │ │ self.generate_direct_templates(write) │
│ 695 │
│ │
│ D:\Code\pelican\pelican\generators.py:518 in generate_articles │
│ │
│ 515 │ │ │ print(f'self.context["generated_content"] =') │
│ 516 │ │ │ pprint(self.context["generated_content"]) │
│ 517 │ │ │ │
│ ❱ 518 │ │ │ write( │
│ 519 │ │ │ │ article.save_as, │
│ 520 │ │ │ │ self.get_template(article.template), │
│ 521 │ │ │ │ self.context, │
│ │
│ D:\Code\pelican\pelican\writers.py:295 in write_file │
│ │
│ 292 │ │ else: │
│ 293 │ │ │ # no pagination │
│ 294 │ │ │ localcontext = _get_localcontext(context, name, kwargs, relative_urls) │
│ ❱ 295 │ │ │ _write_file(template, localcontext, self.output_path, name, override_output) │
│ 296 │
│ │
│ D:\Code\pelican\pelican\writers.py:215 in _write_file │
│ │
│ 212 │ │ │ # set localsiteurl for context so that Contents can adjust links │
│ 213 │ │ │ if localcontext["localsiteurl"]: │
│ 214 │ │ │ │ context["localsiteurl"] = localcontext["localsiteurl"] │
│ ❱ 215 │ │ │ output = template.render(localcontext) │
│ 216 │ │ │ path = sanitised_join(output_path, name) │
│ 217 │ │ │ │
│ 218 │ │ │ try: │
│ │
│ D:\Code\obsidian-vault\.venv\Lib\site-packages\jinja2\environment.py:1295 in render │
│ │
│ 1292 │ │ try: │
│ 1293 │ │ │ return self.environment.concat(self.root_render_func(ctx)) # type: ignore │
│ 1294 │ │ except Exception: │
│ ❱ 1295 │ │ │ self.environment.handle_exception() │
│ 1296 │ │
│ 1297 │ async def render_async(self, *args: t.Any, **kwargs: t.Any) -> str: │
│ 1298 │ │ """This works similar to :meth:`render` but returns a coroutine │
│ │
│ D:\Code\obsidian-vault\.venv\Lib\site-packages\jinja2\environment.py:942 in handle_exception │
│ │
│ 939 │ │ """ │
│ 940 │ │ from .debug import rewrite_traceback_stack │
│ 941 │ │ │
│ ❱ 942 │ │ raise rewrite_traceback_stack(source=source) │
│ 943 │ │
│ 944 │ def join_path(self, template: str, parent: str) -> str: │
│ 945 │ │ """Join a template with the parent. By default all the lookups are │
│ │
│ D:\Code\obsidian-vault\.venv\Lib\site-packages\pelican\plugins\seafoam\templates\article.html:1 │
│ in top-level template code │
│ │
│ ❱ 1 {% extends "base.html" %} │
│ 2 │
│ 3 {% block title -%} │
│ 4 │ {{ article.title | striptags | breaking_spaces }} — {{ super() }} │
│ │
│ D:\Code\obsidian-vault\.venv\Lib\site-packages\pelican\plugins\seafoam\templates\base.html:261 │
│ in top-level template code │
│ │
│ 258 │ │ {% endblock %} │
│ 259 │ {% endif %} │
│ 260 │ │
│ ❱ 261 │ {% block content %} │
│ 262 │ {% endblock %} │
│ 263 │ │ │
│ 264 │ │ </div> │
│ │
│ D:\Code\obsidian-vault\.venv\Lib\site-packages\pelican\plugins\seafoam\templates\article.html:12 │
│ 3 in block 'content' │
│ │
│ 120 │ │ │ │ {% if article.gpx %} │
│ 121 │ │ │ │ │ {# for a GPX "Article", the "content" is the raw GPX file #} │
│ 122 │ │ │ │ {% else %} │
│ ❱ 123 │ │ │ │ │ {{ article.content }} │
│ 124 │ │ │ │ {% endif %} │
│ 125 │ │ │ </div> <!-- /.entry-content --> │
│ 126 │ │ │ {% include 'includes/neighbors.html' %} │
│ │
│ D:\Code\obsidian-vault\.venv\Lib\site-packages\jinja2\environment.py:490 in getattr │
│ │
│ 487 │ │ Unlike :meth:`getitem` the attribute *must* be a string. │
│ 488 │ │ """ │
│ 489 │ │ try: │
│ ❱ 490 │ │ │ return getattr(obj, attribute) │
│ 491 │ │ except AttributeError: │
│ 492 │ │ │ pass │
│ 493 │ │ try: │
│ │
│ D:\Code\pelican\pelican\contents.py:452 in content │
│ │
│ 449 │ │
│ 450 │ @property │
│ 451 │ def content(self) -> str: │
│ ❱ 452 │ │ return self.get_content(self.get_siteurl()) │
│ 453 │ │
│ 454 │ @memoized │
│ 455 │ def get_summary(self, siteurl: str) -> str: │
│ │
│ D:\Code\pelican\pelican\\\utils.py:161 in __call__ │
│ │
│ 158 │ │ if args in self.cache: │
│ 159 │ │ │ return self.cache[args] │
│ 160 │ │ else: │
│ ❱ 161 │ │ │ value = self.func(*args) │
│ 162 │ │ │ self.cache[args] = value │
│ 163 │ │ │ return value │
│ 164 │
│ │
│ D:\Code\pelican\pelican\contents.py:448 in get_content │
│ │
│ 445 │ │ │ content = self._get_content() │
│ 446 │ │ else: │
│ 447 │ │ │ content = self._content │
│ ❱ 448 │ │ return self._update_content(content, siteurl) │
│ 449 │ │
│ 450 │ @propertySource: getpelican/pelican