CollabOps
CI/CDTroubleshooting

On-Prem Troubleshooting

Common build and deploy errors in air-gapped on-prem environments — Nexus mirror, internal CA, Docker registry, WAS, JDK, settings.xml, RBAC, approval gate

This page is a troubleshooting guide for common build and deploy errors when adopting collabops in air-gapped on-prem environments (government, finance). Each entry shows symptom, cause, and fix across categories like internal Nexus, certificates, JDK versions, and WAS deployment.

1. Dependencies / Nexus mirror

Could not transfer artifact — Maven Central blocked

Symptom: [ERROR] Failed to execute goal ... Could not transfer artifact com.example:foo:pom:1.0.0 from/to central (https://repo.maven.apache.org/maven2): Connection refused

Cause: Air-gapped environments block direct access to Maven Central. settings.xml has no mirror, so Maven tries the default Central URL.

<!-- settings.xml -->
<mirrors>
  <mirror>
    <id>nexus-internal</id>
    <name>Internal Nexus</name>
    <url>https://<nexus-host>/repository/maven-public</url>
    <mirrorOf>*</mirrorOf>
  </mirror>
</mirrors>

401 Unauthorized — missing Nexus server credentials

Symptom: Authentication failed for ... 401 Unauthorized

Cause: settings.xml has the mirror but no matching server credentials. The server id must match the mirror id for Maven to apply auth.

<servers>
  <server>
    <id>nexus-internal</id>
    <username>${env.NEXUS_USERNAME}</username>
    <password>${env.NEXUS_PASSWORD}</password>
  </server>
</servers>

2. Certificates / internal CA

PKIX path building failed — internal CA missing in truststore

Symptom: javax.net.ssl.SSLHandshakeException: ... PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Cause: The internal CA certificate is not in the JDK cacerts truststore, so Maven / Gradle cannot trust the private Nexus HTTPS cert.

# Import the internal CA into the JDK truststore
keytool -import -trustcacerts \
  -alias corp-ca \
  -file /path/to/corp-ca.crt \
  -keystore $JAVA_HOME/lib/security/cacerts \
  -storepass changeit -noprompt

# For container builds, do the same in the Dockerfile
# COPY corp-ca.crt /tmp/
# RUN keytool -import -trustcacerts -alias corp-ca \
#     -file /tmp/corp-ca.crt \
#     -keystore $JAVA_HOME/lib/security/cacerts \
#     -storepass changeit -noprompt

3. Docker registry / air-gapped

no such host: registry-1.docker.io — Docker Hub blocked

Symptom: Error response from daemon: pull access denied for maven, repository does not exist or may require 'docker login': no such host: registry-1.docker.io

Cause: Air-gapped environments cannot reach Docker Hub. The Dockerfile base image still points to Docker Hub.

# Route through the internal Harbor mirror
ARG HARBOR_URL
FROM ${HARBOR_URL}/library/maven:3.9-eclipse-temurin-17 AS build
# ...
FROM ${HARBOR_URL}/library/eclipse-temurin:17-jre

unauthorized: authentication required — missing Harbor auth

Symptom: Error response from daemon: unauthorized: authentication required

Cause: Harbor robot account credentials are missing or expired. The docker-build-push template cannot find .docker/config.json in the build context.

# Add a harbor-login step in pipeline.yaml (see the java-spring-boot-harbor-image starter-workflow on Marketplace)
- name: harbor-login
  image: alpine:3.19
  env:
    HARBOR_URL: "${{ secrets.HARBOR_URL }}"
    HARBOR_USERNAME: "${{ secrets.HARBOR_USERNAME }}"
    HARBOR_PASSWORD: "${{ secrets.HARBOR_PASSWORD }}"
  run: |
    set -euo pipefail
    auth=$(printf '%s:%s' "$HARBOR_USERNAME" "$HARBOR_PASSWORD" | base64 | tr -d '\n')
    mkdir -p /workspace/source/.docker
    printf '{"auths":{"%s":{"auth":"%s"}}}' "$HARBOR_URL" "$auth" \
      > /workspace/source/.docker/config.json

4. WAS deployment (Tomcat / WebLogic / JEUS)

NoClassDefFoundError: jakarta/servlet/Filter — external Tomcat conflict

Symptom: java.lang.NoClassDefFoundError: jakarta/servlet/Filter (or javax.servlet.* not found)

Cause: The WAR was built with spring-boot-starter-tomcat in compile scope, so its servlet classes clash with the external Tomcat. The Servlet API must come from the external WAS.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <scope>provided</scope>
</dependency>

context root exposes the ##version suffix

Symptom: After Tomcat parallel deployment, the URL looks like /app##20260518000000/ (intended: /app).

Cause: Wrong WAR filename format. With &lt;context-root&gt;##&lt;version&gt;.war, only the part before ## is the context root. If the WAR is named incorrectly, the ##version leaks into the URL.

# WAR filename format: <context-root>##<version>.war
# e.g. webapps/app##20260518000000.war  → context root = /app
cp /tmp/app.war "${CATALINA_BASE}/webapps/app##$(date -u +%Y%m%d%H%M%S).war"

5. Java / JDK version

UnsupportedClassVersionError — compile / runtime JDK mismatch

Symptom: java.lang.UnsupportedClassVersionError: ... has been compiled by a more recent version of the Java Runtime (class file version 61), this version of the Java Runtime only recognizes class file versions up to 55

Cause: Compiled with JDK 17 (class file 61) but executed on JDK 11 (class file 55). Build image and runtime image have different JDK majors.

<!-- pom.xml -->
<properties>
  <java.version>17</java.version>
</properties>

<!-- Use the same JDK major in the runtime image -->
<!-- FROM ${HARBOR_URL}/library/eclipse-temurin:17-jre -->

Spring Boot 3 — missing javax → jakarta migration

Symptom: After upgrading to Spring Boot 3.x, package javax.servlet does not exist or other import errors.

Cause: Spring Boot 3 uses Jakarta EE 9+, which renames javax.* packages to jakarta.*. Code still importing javax.* fails to compile.

// Before (Spring Boot 2.x)
import javax.servlet.http.HttpServletRequest;
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;

// After (Spring Boot 3.x)
import jakarta.servlet.http.HttpServletRequest;
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotNull;

// Use OpenRewrite recipes or IDE refactoring for bulk replacement

6. Maven settings.xml

mvn deploy 401 — distributionManagement id mismatch

Symptom: On mvn deploy: Failed to deploy artifacts: Could not transfer artifact ... 401 Unauthorized

Cause: The &lt;id&gt; under pom.xml &lt;distributionManagement&gt; does not match any &lt;server&gt;&lt;id&gt; in settings.xml. Maven cannot pick the right credentials.

<!-- pom.xml -->
<distributionManagement>
  <repository>
    <id>nexus-internal</id>     <!-- must match -->
    <url>https://<nexus-host>/repository/maven-releases</url>
  </repository>
</distributionManagement>

<!-- settings.xml -->
<servers>
  <server>
    <id>nexus-internal</id>     <!-- ← same id as above -->
    <username>${env.NEXUS_USERNAME}</username>
    <password>${env.NEXUS_PASSWORD}</password>
  </server>
</servers>

7. Permissions / RBAC

Insufficient workflow execution permission — workspace role / branch protection

Symptom: A push does not create a workflow run, or manual execution returns 'permission denied'.

Cause: The actor lacks the required workspace role, or main is a protected branch without direct push access, or the workflow trigger's branches filter does not match.

# Check, in order:
# 1. workspace role: member / maintainer / owner — which level the actor has
# 2. branch protection: is direct push to main allowed, or must changes go through a CR
# 3. workflow triggers branches / tags filters — do they match the actual push

# If permission is insufficient, ask the workspace admin to raise the role or change branch protection

8. Approval gate (workflow_dispatch)

workflow_dispatch blocked — actor not in allowlist

Symptom: On prd workflow_dispatch, the verify-actor step emits a permission-denied error (printed in Korean): ERROR: &lt;user&gt; 는 prd 배포 권한이 없습니다

Cause: The verify-actor step of the java-spring-boot-multienv-promote starter-workflow (Marketplace) compares the executor's username against workspace vars PRD_DEPLOY_ALLOWED_ACTORS. The current actor is not on the list.

# Ask a workspace admin to add your username to PRD_DEPLOY_ALLOWED_ACTORS
# before: vars.PRD_DEPLOY_ALLOWED_ACTORS = "alice,bob,charlie"
# after:  vars.PRD_DEPLOY_ALLOWED_ACTORS = "alice,bob,charlie,dave"

# A first-class step-level approval gate (reviewer group / timeout / audit log)
# is planned for after collabops workflow environment protections land

Table of Contents