For the complete documentation index, see llms.txt. This page is also available as Markdown.

Common issues

The failures that come up most often when running the Seal CLI in CI.

The Seal CLI fails the same way in CI as it does on a developer's machine. A few specific issues come up disproportionately often in pipeline contexts.

Network issues

The CLI cannot reach Seal's servers. The runner needs HTTPS access to the Seal Platform and the Seal Artifact Server. The most common blockers:

  • A corporate firewall or egress proxy is filtering outbound HTTPS by hostname. Allow-list Seal's hostnames at the egress point.

  • A zero-trust network product such as ZScaler, Netskope, or similar is intercepting outbound traffic and rejecting Seal's servers, or rewriting the connection in a way the CLI cannot complete. Add Seal's hostnames to the product's allow-list, or run the CLI on a runner that is not behind the inspection layer.

  • The CI runner is in a private subnet without outbound internet. Either give the runner egress, or move to the on-prem workflow described in On-prem and air-gapped environments.

For fully air-gapped environments, see the same chapter for the air-gapped variants.

Authentication errors

SEAL_TOKEN is not set or has been revoked. Confirm the variable is exported in the step's environment, that the token is a current value (not a rotated one), and that the token is the right type for the activity (production for production-equivalent CI, development for developer machines and feature-branch CI).

If the CI step is being run by a forked pull request, your CI platform may not expose secrets to runners by default; the runner sees SEAL_TOKEN as empty even though it is set at the project level. Check your platform's fork-PR secrets handling.

CLI misconfiguration

The wrong fix mode. seal fix --mode remote fetches rules from the Seal Platform; seal fix --mode local reads them from .seal-actions.yml; seal fix --mode all ignores both and seals every vulnerable package. Mixing them up (for example, running in local mode while your rules live in the platform UI) results in no rules being applied. Match the mode to where your rules actually live.

The wrong SEAL_PROJECT. Two unrelated CI pipelines running with the same SEAL_PROJECT clobber each other's results in Seal's view. See the Anti-patterns section in Creating a Seal Project.

The manifest is not auto-detected. The CLI looks in the working directory by default. If your manifest is elsewhere, pass it with --manifest <path> on the CLI invocation, or cd into the directory before running. Common case: a monorepo where the relevant lockfile is several directories deep.

The wrong manifest is scanned. The CLI parses the file it finds first. For npm, package-lock.json is the right input; package.json does not pin versions and is rejected. For Python, both requirements.txt and Pipfile.lock are valid. If the CLI scans the wrong one, point at the right one with --manifest.

Step placement

The CLI runs before the dependency-install step. A common mistake is to put seal fix before npm install / mvn install / pip install. The CLI replaces packages on disk that the install step has already put there; if no packages are installed yet, there is nothing to replace and the seal step is effectively a no-op. Run seal fix after the install step.

The CLI runs after the packaging step. The mirror image of the above. If seal fix runs after the step that bundles or compiles the dependencies into your final artifact (webpack, JAR packaging, docker build, and so on), the packaged output is already built from the vulnerable versions. Run seal fix before the packaging step.

The CLI runs on the wrong commit. In branch-based CI, the CLI runs against whatever is checked out. If your workflow checks out a different ref before the CLI step, the scan and the build can diverge.

Sealing Rules apply but the build still uses vulnerable versions

A common report. The usual causes:

  • You are looking at an artifact the current build did not produce. A binary that was uploaded earlier, or built in a previous iteration of the pipeline, will still carry whatever was in it at build time. Confirm the artifact under inspection is the one this build wrote.

  • The Sealing Rule does not match the package. The rule targets a specific origin version; if your manifest pins a slightly different version, or the rule was created in a different Seal Project, it does not apply to this run.

  • The CLI fix mode does not match where the rules are stored. local reads .seal-actions.yml and ignores the platform UI; remote does the opposite. A remote-stored rule plus a local fix-mode call is a no-op.

  • A cache is serving stale artifacts. The runner's package-manager cache, or an internal artifact-server cache between the runner and Seal, can return a previously-pulled vulnerable version instead of the sealed one. This is most common in Java (older Maven and Gradle configurations cache aggressively). Clear the cache and re-run.

  • The vulnerability is not one Seal remediates. Not every CVE has a sealed counterpart. See Partial fixes & open vulnerabilities for the reasons.

Platform-specific gotchas

GitHub Actions: secrets are not exposed to runners triggered by pull requests from forks. The CLI step in those PR runs sees SEAL_TOKEN as empty. Either run the seal step on push triggers only, or configure your repository's fork-PR secrets handling explicitly.

Jenkins: the credentials() binding sets the environment variable only inside the step that declares it. If your Jenkinsfile splits the install and the run into separate stages, both need the binding.

CircleCI: project-level environment variables and context-level variables behave the same in normal jobs but interact differently with restricted contexts. If a job that worked locally fails in the protected context, confirm the token is visible there.

Azure DevOps: pipeline variables marked secret are not exposed to the shell environment by default. The script step has to explicitly export SEAL_TOKEN=$(SEAL_TOKEN) to make the value visible to the CLI.

Docker (any CI): when running the CLI inside a Dockerfile via RUN, BuildKit caching can keep the CLI step's output from re-running when you expect it to. Either invalidate the cache (touch the dependency file before the CLI step, or pass --no-cache to docker build) or split the CLI step into a stage whose output you do not cache.

Last updated