How to fix the python attempted relative import error when deploying a packaged bot, using StayPresent's bot_module parameter instead of bot_file.
Fixing "Attempted Relative Import" Errors with StayPresent's bot_module
If your bot is structured as a proper Python package — with relative imports like from . import handlers scattered across its modules — you've probably hit this the moment you tried to run it directly:
ImportError: attempted relative import with no known parent package
This is one of the most common attempted relative import errors developers run into when deploying a packaged bot, and it has nothing to do with your bot's logic — it's purely about how the script gets launched. This guide covers why it happens and how StayPresent's bot_module parameter fixes it without restructuring your project.
Table of Contents
- Why the Error Happens
-
python file.pyvspython -m module - Using
bot_module - Multiple Module-Based Bots
- Mixing File-Based and Module-Based Bots
- What's Different From
bot_file - Full Example
- Best Practices
- Common Mistakes
- FAQs
- Conclusion
Why the Error Happens
Python treats a script run directly (python bot.py) as a top-level module with no package context — it has no __package__, so any from . import something inside it (or inside anything it imports) fails immediately, because there's no "parent package" for the dot to refer to. This has nothing to do with your file structure being wrong; it's purely a consequence of how the interpreter was invoked.
python file.py vs python -m module
Running python mypkg/bot.py executes the file as a standalone script. Running python -m mypkg.bot instead executes it as a module inside its package, with __package__ set correctly — which is exactly what relative imports need to resolve.
project/
├── mypkg/
│ ├── __init__.py
│ ├── bot.py # uses `from . import handlers`
│ └── handlers.py
python mypkg/bot.py # ImportError: attempted relative import
python -m mypkg.bot # works correctly
Using bot_module
staypresent.run()'s bot_file parameter runs your bot the same way python <file> would — which means it inherits this exact limitation. bot_module runs it the way python -m <module> would instead:
import staypresent
staypresent.run(bot_module="mypkg.bot")
No changes to your bot's own code are needed — the fix is entirely in how StayPresent launches it.
Multiple Module-Based Bots
Just like bot_file, bot_module accepts a list for multiple bots, each independently supervised:
staypresent.run(bot_module=["mypkg.bot", "mypkg.worker"])
bot_args/env apply identically to every module in the list, exactly as they would with bot_file.
Mixing File-Based and Module-Based Bots
Inside the bots parameter, use "module" instead of "file" per entry — and you can freely combine both styles in the same call:
import staypresent
staypresent.run(bots=[
{"file": "telegram_bot.py", "args": ["--verbose"]},
{"module": "discord_bot.worker", "env": {"SHARD": "0"}},
])
Each entry must set exactly one of "file"/"module" — setting both, or neither, raises TypeError.
What's Different From bot_file
A few important differences to be aware of:
-
No existence check up front. A file path can be checked for existence before anything starts; a module path genuinely can't be verified as importable without actually importing it — which would mean running its parent packages'
__init__.pyas a side effect inside the orchestrating process, something StayPresent deliberately avoids. A missing or misspelled module isn't caught until that bot subprocess actually starts and Python itself reportsNo module named '...'— which is then handled through the normal crash/restart logic like any other failure. -
Working directory matters. The subprocess is launched from the same working directory as your orchestrating script.
bot_module="mypkg.bot"needs to be resolvable from wherever you actually run yourmain.py, exactly as if you'd typedpython -m mypkg.botfrom that same location yourself. -
Log labels use the dotted module path. Instead of a filename, log lines show something like
bot[0] 'mypkg.bot'. If two bots share the exact same module string, there's no further-qualifying fallback the way there is for file paths (no "directory" to disambiguate with) — those log lines are distinguished only by theirbot[i]index.
Full Example
import os
import staypresent
staypresent.web.json({"status": "running"})
staypresent.run(
bot_module="mypkg.bot",
port=int(os.getenv("PORT", 8080)),
restart_on_crash=True,
max_restarts=5,
)
Best Practices
- If your bot already runs correctly locally via
python -m mypkg.bot, usebot_moduleinstaypresent.run()rather than trying to work around relative imports withsys.pathhacks. - Keep your deployment's working directory consistent between local testing and production — if
python -m mypkg.botonly works from a specific directory locally, that same constraint applies to your hosting platform's start command. - Since module paths aren't validated up front, watch your logs closely on the very first deploy of a module-based bot to catch a typo'd module path quickly.
Common Mistakes
-
Passing a file path to
bot_module(e.g.bot_module="mypkg/bot.py") instead of a dotted module path ("mypkg.bot") — this will fail the same way runningpython -m mypkg/bot.pywould from the command line. -
Assuming
bot_modulevalidates the module exists before starting, and being surprised when a typo only surfaces as a crash-and-restart cycle rather than an immediate, upfront error. -
Running the orchestrating script from the wrong directory, so
mypkg.botcan't be found even though the code itself is correct.
FAQs
Do I need to change my bot's code to use bot_module?
No — if your bot already works when launched with python -m, no code changes are needed at all.
Can I mix bot_file and bot_module at the top level of run()?
No, they're mutually exclusive at the top level — use the bots list if you need both styles in the same deployment.
Why doesn't StayPresent just validate the module up front like it does for files?
Verifying a module is importable requires actually importing it, which means executing its package's __init__.py as a side effect — something StayPresent avoids doing inside the process that's supposed to just be orchestrating, not running, your bot's code.
Conclusion
The attempted relative import error is one of the most common surprises when deploying a properly packaged bot. bot_module solves it by launching your bot exactly the way python -m would, with no restructuring of your project and no sys.path workarounds required.
pip install staypresent[prod]
Top comments (0)