Resource Files, Variable Files, Library Imports, and Project Layout: Diagnostics, Failure Modes, and Production Practices
Diagnose import and layout failures systematically: preserve first evidence, identify the exact resolver and source, then repair cycles, collisions, cwd dependence, module shadowing, variable-file side effects, and output contamination.
Learning objectives
- Apply a layered diagnostic sequence before changing import paths.
- Differentiate missing-resource, missing-library, collision, variable-file, and working-directory failures.
- Diagnose Python module shadowing using import provenance rather than PYTHONPATH guesswork.
- Preserve first-failure results and avoid import-time secret leakage.
- Repair the smallest ownership boundary and rerun a controlled slice.
Current compatibility baseline. Verified
2026-08-31: Robot Framework 7.4.2 is the current stable release and
requires Python 3.8+; 7.5b1 is a pre-release and is not required in
this chapter. In 7.4.2, .resource is the recommended
resource-file extension. Resource and Settings-level variable-file
paths are resolved relative to the importing data file first and
then via Python's module search path. ${CURDIR} is the
absolute directory of the current data file;
${EXECDIR} is the absolute directory where execution
started. JSON variable files require no optional package; YAML
variable files require PyYAML.
1. Diagnostic sequence: preserve first evidence, then identify the resolver
-
Preserve console output and the current
results/directory; do not overwrite it. -
Record Python/Robot versions, executable path, cwd, and
sys.path. -
Confirm the exact suite path and command-line
--pythonpath/--variablefileoptions. - Run
--dryruninto a new result directory. - Trace each failed Resource/Variables/Library import from the importing file outward.
- Inspect keyword qualification and variable-source precedence only after imports succeed.
- Inspect external-system/parallel/CI/container state only if the import graph is clean.
- Apply the least destructive correction and rerun the smallest suite/test with a fresh output directory.
2. Failure map
| Symptom | Likely layer | Evidence |
|---|---|---|
| Resource file not found | Source-relative path or module search path |
Importer path, resource setting, --pythonpath
|
| Library module not found | Python environment/module search path |
sys.executable, sys.path,
module.__file__
|
| Keyword is ambiguous | Resource/library resolution | Imported resources/libraries + full keyword names |
| Variable changes by runner | Variable precedence or import-time behavior | Variable-file order, CLI values, environment snapshot |
| Works only from repo root | cwd/EXECDIR-dependent path | Compare commands and logged EXECDIR/CURDIR |
| YAML import fails only in clean env | Missing optional PyYAML | Pinned dependencies + import error |
| Source tree becomes dirty after tests | Output placement | --outputdir and git status |
3. Intentionally broken path: cwd dependency disguised as convenience
*** Settings ***
# INTENTIONALLY BRITTLE — resolved by the process environment, not the owning source file.
Variables variables/demo.py
If this file lives under tests/, the path does not mean
“project-root/variables” simply because you normally launch from the
project root. Settings-level variable paths are source-relative
first. Repair it explicitly as ../variables/demo.py or
establish a documented module-search/package contract.
4. Duplicate resource keywords: make the ambiguity observable
*** Settings ***
Resource ../resources/orders.resource
Resource ../resources/users.resource
*** Test Cases ***
Ambiguous example
Normalize ID abc-123
If both resources define Normalize ID, Robot cannot
choose by folder intent. Preserve the failure, then qualify the
call:
orders.Normalize ID order-123
users.Normalize ID user-123
The production follow-up is to ask whether the names should be more domain-specific. Qualification is transparent; accidental import-order dependence is not.
5. Circular resource imports: stop following the loop and redraw ownership
# resources/a.resource
*** Settings ***
Resource b.resource
# resources/b.resource
*** Settings ***
Resource a.resource
A cycle means neither resource is a lower-level dependency. Do not
add dynamic Import Resource calls or search-path tricks
to hide it. Extract the shared keyword/value into
shared.resource and have a.resource and
b.resource import that lower layer. The repaired graph
should be acyclic and reviewable.
6. Python module shadowing: prove which module was imported
A local file named json.py, robot.py, or
the same name as a third-party library can shadow the intended
module depending on sys.path. Diagnose with Python
import provenance:
python -c "import os,sys; sys.path.insert(0, os.environ['RF12_ROOT']); import libraries.text_tools as m; print(m.__file__)"
python -c "import sys; print('\n'.join(sys.path))"
Do not fix shadowing by prepending arbitrary directories until the desired file happens to win. Rename the conflicting module or package the code so import identity is explicit.
7. Variable-file side effects: imports should not become hidden setup
# INTENTIONALLY UNSAFE DESIGN — do not use for production configuration.
import os
print("token=", os.environ.get("REAL_TOKEN"))
# network_call_to_discover_environment() # hidden external side effect
This is dangerous because import happens before the scenario has a visible setup/teardown boundary. It can leak secrets to console/logs and make dry-run/environment behavior surprising. Replace it with static/synthetic values or an explicit runtime keyword that can redact evidence and own cleanup.
8. Missing YAML dependency: optional format means optional package
Robot Framework core has no mandatory dependency beyond Python, but
YAML variable files require PyYAML. A clean CI environment that
installs only robotframework==7.4.2 can therefore fail
to load variables.yaml. Either pin PyYAML in the
project dependencies or use JSON for dependency-free structured
values. Do not “fix CI” by installing unpinned packages
interactively on the runner.
9. Results in source directories: diagnose contamination with git status
git status --short
# Expected after a clean run: only intended source edits.
# output.xml/log.html/report.html should be under results/, not tests/.
If generated evidence appears beside source files, correct the
runner with an explicit --outputdir. Do not add broad
ignore rules that hide arbitrary generated files everywhere; keep
the output root deliberate.
10. Brittle ../../ chains: count ownership boundaries, not dots
A path such as
../../../../shared/common.resource signals that the
importer depends on repository topology rather than a nearby owner.
Before replacing it with --pythonpath, ask whether the
resource belongs in a package-like shared root, whether the suite is
too deeply nested, or whether the dependency belongs behind a domain
resource. Fix architecture first; configure the search path second.
11. Troubleshooting shortcuts to reject
-
Appending arbitrary directories to
PYTHONPATHuntil an import succeeds. - Using global Robot variables to “share” configuration around a broken import graph.
- Deleting the first failing result directory before comparing the repair.
- Disabling TLS/SSH verification or contacting production systems to “test the real path.”
- Using broad EXCEPT/retries around import-dependent runtime keywords.
- Renaming output files into source directories instead of defining an output root.
- Logging real environment secrets to prove a variable file loaded.
12. Diagnostic exercise: two failures, two layers
Start with the clean Chapter 12 lab. Then introduce these changes separately:
-
Change
Resource ../resources/labels.resourcetoResource labels.resource. Run dry-run from an unrelated cwd and preserve the “not found” evidence. Repair the source-relative path. -
Add a second resource defining
Label Should Normalize. Preserve the ambiguity, qualify the call, then rename/refactor to restore one domain owner.
Do not change both failures at once. One controlled variable per diagnostic run keeps causality visible.
13. Knowledge check
A suite works from the IDE but not CI. What should you inspect before editing imports?
The exact interpreter, Robot version, cwd/EXECDIR, sys.path, command-line --pythonpath, and importing file path. Prove which resolver changed.
Why is “add the repo root to PYTHONPATH globally” a weak emergency fix?
It can hide module shadowing and unclear dependency ownership, affects unrelated Python processes, and does not document the Robot runner contract.
How do you prove Python imported the expected custom library?
Import it with the same interpreter/search path and inspect its __file__, then compare with the intended project/package location.
Why should a YAML-only failure in clean CI not be treated as a Robot parser bug?
YAML variable files depend on PyYAML. The dependency layer must be verified first.
14. Summary and next step
Import incidents become tractable when you preserve evidence and identify the resolver before changing paths. Source-relative resource/variable paths, Python module discovery, keyword resolution, variable-file behavior, and output placement are distinct layers. Lesson 5 combines them in a checkpoint: run from two directories, inject one path failure and one keyword collision, repair both, and deliver a dependency diagram plus evidence packet.
Further reading
- Robot Framework 7.4.2 User Guide — Resource and variable files — resource structure, import rules, Python/YAML/JSON variable files, and precedence.
- Robot Framework 7.4.2 User Guide — Using test libraries — importing libraries by name or path and library aliases.
-
Robot Framework 7.4.2 User Guide — Module search path
— installed packages, PYTHONPATH, and
--pythonpath. -
Robot Framework 7.4.2 User Guide — Built-in variables
—
${CURDIR},${EXECDIR}, path separators, and temporary directory. - Robot Framework 7.4.2 User Guide — Handling keywords with same names — scope priority, explicit qualification, and search order.
- Robot Framework 7.4.2 User Guide — Libdoc — library/resource documentation and import verification.
- Robot Framework 7.4.2 on PyPI — pinned stable package metadata and Python requirement.
- Robot Framework releases — re-check stable/pre-release status when updating the chapter.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.