Troubleshooting
PowerShell Version Is Unsupported
Symptom: manifest import or baseline validation reports a runtime below 7.4.
$PSVersionTable.PSVersion
Install PowerShell 7.4 or later. Test-PowerShellBaseline.ps1 intentionally requires
exactly 7.4.x even when development uses a newer runtime.
Specification Was Not Found
Default path:
PSModule/PSModule.psd1
Select another file:
Build-PSModule -Specification ./config/MyModule.psd1
Or initialize:
Initialize-PSModuleSpecification -Directory .
Validation Fails
Run validation independently:
Test-PSModuleSpecification -Specification ./PSModule/PSModule.psd1
Common causes:
- a collection is a scalar hashtable instead of
@(...); - duplicate command, parameter, or object IDs;
- invalid
Verb-Nouncommand syntax; - a parameter type contains unsupported syntax;
Mandatoryis not Boolean;- an unknown typed validation, completion, or mapping;
- a mapping property does not match its parameter type; or
- invalid help or example strings.
Generated Output Path Is Rejected
PSGenerator checks the selected output immediately before any reset. Use the message to distinguish these cases:
| Symptom or cause | Recovery |
|---|---|
| Output is a filesystem root | Choose a dedicated generated-output subdirectory. Force cannot permit a root. |
| Output is the source directory, the specification directory, or an ancestor | Choose output outside that relationship, normally artifacts/PSModule. Force cannot permit it. |
| Output is an existing file | Select a directory path, or move/rename the file first. |
| Output is a symbolic link or junction | Select the real dedicated directory directly. Linked output leaves are never reset. |
| Non-empty output is not recognized as generated | Inspect and move valuable data, choose another directory, or deliberately rebuild with -Force. |
Metadata/output.json is malformed or has unexpected values | Treat the directory as unowned. Prefer another directory or preserve its contents; use -Force only after review. |
Output overlaps the source scripts tree | Choose output outside scripts. Force cannot permit a recursive packaging relationship. |
| Output changed while reset was being validated | Stop concurrent writers and retry with a stable, dedicated directory. |
If a previously generated package is rejected only because
Metadata/output.json was deleted, inspect the package and perform one deliberate
rebuild to restore ownership metadata:
Build-PSModule -Output ./artifacts/PSModule -Force
If marker creation fails after reset, check directory permissions, free space, and
filesystem health for Metadata/output.json, then retry. The prior output has already
been reset at that point; there is no transactional rollback.
Initialize-PSModuleDirectory uses -ForceOutput for the same output-only adoption:
Initialize-PSModuleDirectory -Directory . -Generate -ForceOutput
No Inferred Commands Appear
Inference reads only scripts/**/*.ps1 and explicitly exported functions in
scripts/**/*.psm1.
Check parse errors:
$inspection = Get-PSModuleInspection
$inspection.Data.PowerShellFiles |
Select-Object Path, IsCommandCandidate, SuggestedCommandName, ParseErrors
Move intended public entry points beneath scripts, fix parsing, and explicitly
export module functions.
Scaffold Does Not Refresh
The local directory harness preserves authored specifications and any scaffold with runtime mappings. This prevents inference from overwriting intent.
To deliberately replace a specification:
Initialize-PSModuleSpecification `
-Directory . `
-Force
Review or save the existing PSD1 first.
Inferred Command Collides with an Existing Command
Symptom: initialization warns that a script-derived command may shadow an existing command after import.
The warning is advisory. The inferred command remains in the generated specification, and repeated generation remains byte-identical. Resolve intentional shadowing by doing either of the following:
- rename the source script; or
- author the specification explicitly with a different command name.
Collision discovery inspects the current session and literal exports read from
conventional manifests beneath PSModulePath. It does not analyze or import
available root modules. Commands exported dynamically by module code may therefore
be absent from the warning.
An earlier PSGenerator-generated module is ignored only when its module name, generator provenance, and specification ID all match. An unrelated module with the same name still produces a warning.
Generated Command Calls Docker Unexpectedly
Only commands with SourceKind = 'Script' or ModuleFunction use packaged local
source. A SourcePath alone does not select a script inside a container.
Inspect:
$model = Get-PSModuleModel
$model.Commands |
Select-Object Name, @{n='SourceKind';e={$_.Definition.SourceKind}},
@{n='SourcePath';e={$_.Definition.SourcePath}}
Regenerate the scaffold from a source beneath scripts or author explicit container
mappings.
Packaged Script Cannot Find a Sibling File
Scripts should resolve dependencies relative to their own location:
$commonModule = Join-Path $PSScriptRoot 'modules/Common.psm1'
Import-Module $commonModule -Force
Do not use $PWD for packaged-source dependencies; it refers to the caller's current
directory, not the script directory.
Docker Is Missing
Get-Command docker
docker info
-WhatIf does not require Docker:
Invoke-MyCommand -WhatIf
Install-PSModule example/image:latest -WhatIf
Docker Exits Non-zero
Run with verbose tracing:
Invoke-MyCommand -Verbose
Copy the reported arguments into a direct Docker invocation, then inspect:
- image availability;
- entry point and command contract;
- host path existence and sharing;
- container path permissions;
- port conflicts;
- device/GPU capabilities; and
- resource-limit syntax.
Install-PSModule Fails
The image must contain exactly one top-level manifest at /PSModule.
Inspect manually:
$id = docker create example/image:latest
docker cp "${id}:/PSModule/." ./artifacts/ModuleInspection
docker rm --force $id
Get-ChildItem ./artifacts/ModuleInspection
Test-ModuleManifest ./artifacts/ModuleInspection/*.psd1
Use -Force only when replacing a destination intentionally. Existing destinations
remain untouched when staged validation fails.
Inspector Fails on Directory Data
Run inspection and detailed diagnostics:
$inspection = Get-PSModuleInspection
$inspection | Get-PSModuleDiagnostic -Detailed
Current parsers may terminate on malformed .csproj, package.json,
.nuke/parameters.json, OpenAPI JSON, and authoritative *.schema.json. See
Directory Inspection for supported subsets.
Plugin Fails
The error names the plugin and stage. Confirm:
- filename matches
<numeric-prefix>.<name>.ps1; - the script declares
Context; - the selected root exists only once;
- expected context properties exist at that stage;
- paths are directory-relative; and
- the plugin writes through the context instead of relying on pipeline output.
Local plugins are not sandboxed.
act Differs from GitHub Actions
act runs Linux containers and cannot reproduce hosted Windows. It also uses nested
Docker behavior and shared mounts that differ from hosted runners.
Use act for rapid Linux feedback, direct Pester on Windows for host feedback, and
GitHub Actions as the authoritative cross-platform result.
Package Is Not Visible
Merging the publishing workflow does not publish a package. A published GitHub
Release with a tag matching v<ModuleVersion> triggers publication.
Check:
gh release list
gh run list --workflow publish.yml
If there is no release and no workflow run, no package was pushed.
GitHub Packages Authentication Fails
Consumer operations require credentials. Use a classic PAT with read:packages,
your GitHub username, and the owner feed:
https://nuget.pkg.github.com/The-Running-Dev/index.json
Do not use the publishing workflow's GITHUB_TOKEN outside its workflow run.