#3497·pelican

`self._context` wiped?

Author: minchinwebCreated Sep 13, 2025Updated Sep 13, 2026
Labelsbug
  • 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:

python
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:

python
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 }} &mdash; {{ 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 │   @property