Skip to main content

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-Noun command syntax;
  • a parameter type contains unsupported syntax;
  • Mandatory is 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 causeRecovery
Output is a filesystem rootChoose a dedicated generated-output subdirectory. Force cannot permit a root.
Output is the source directory, the specification directory, or an ancestorChoose output outside that relationship, normally artifacts/PSModule. Force cannot permit it.
Output is an existing fileSelect a directory path, or move/rename the file first.
Output is a symbolic link or junctionSelect the real dedicated directory directly. Linked output leaves are never reset.
Non-empty output is not recognized as generatedInspect and move valuable data, choose another directory, or deliberately rebuild with -Force.
Metadata/output.json is malformed or has unexpected valuesTreat the directory as unowned. Prefer another directory or preserve its contents; use -Force only after review.
Output overlaps the source scripts treeChoose output outside scripts. Force cannot permit a recursive packaging relationship.
Output changed while reset was being validatedStop 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.