Die meisten Capacitor CI/CD-Fehler gehen auf dieselben wenigen Punkte zurück: iOS code Signing, fehlendes oder veraltetes macOS-Tooling, ein Web-Build, der es nie ins Native-Projekt geschafft hat, wiederverwendete Build-Nummern und Store-Anforderungen, die erst beim Upload fehlschlagen. Jeder davon hat eine bekannte Ursache und einen Fix, den du einmal anwendest. Dieser Guide gruppiert die Fallstricke nach Pipeline-Phase – mit der Fehlermeldung, die du siehst, warum sie passiert, und was du ändern musst.
Wenn du eine Pipeline von Grund auf aufsetzt, lies CI/CD-Setup für Capacitor Apps zuerst, und nutzen Sie diese Liste zur Absicherung.
Phase 1: Den Web-Layer bauen
Fehlerquelle: Die native App enthält einen alten Web-Build
Symptom: CI läuft durch, die App wird installiert, zeigt aber das UI von gestern.
Ursache: cap sync kopiert alles, was in webDir in diesem Moment. Wenn die Pipeline läuft cap sync liegt. Wenn die Pipeline vor dem Web-Build läuft oder der Web-Build in einen anderen Ordner schreibt als webDir in capacitor.config.ts, erhält das native Projekt veraltete Dateien.
Lösung: Führe die Schritte immer in dieser Reihenfolge aus und brich ab, falls der Output-Ordner leer ist.
bun install --frozen-lockfile
bun run build
test -f dist/index.html || { echo "web build missing"; exit 1; }
bunx cap sync
Stelle sicher webDir mit deinem Bundler-Output übereinstimmt (dist bei Vite, www bei Angular mit Ionic, build bei einigen React-Setups).
Pitfall: Dev-Server-URL in der Config vergessen
Symptom: Der Release-Build zeigt einen Blank Screen oder versucht zu laden http://192.168.x.x:5173.
Ursache: server.url in capacitor.config.ts für Live Reload gesetzt und committet wurde.
Lösung: niemals committen server.url. Lies es aus einer Umgebungsvariable, die CI nie setzt:
import type { CapacitorConfig } from '@capacitor/cli'
const config: CapacitorConfig = {
appId: 'com.example.app',
appName: 'Example',
webDir: 'dist',
...(process.env.LIVE_RELOAD_URL && {
server: { url: process.env.LIVE_RELOAD_URL, cleartext: true },
}),
}
export default config
Falle: Umgebungsvariablen zum falschen Zeitpunkt eingebettet
Symptom: die Produktions-App kommuniziert mit dem Staging API.
Ursache: Vite, webpack und Angular binden Umgebungsvariablen beim Build inline ein. Der Wert, der zum Zeitpunkt des bun run build Durchlaufs vorliegt, landet in der Binary und in jedem live update, der aus demselben Job gebaut wurde.
Lösung: Setze umgebungsspezifische Variablen vor dem Web-Build pro Job und erstelle separate Bundles für Staging und Production. Versuche nicht, diese nachträglich auszutauschen. cap sync.
Problem: Lockfile-Drift
Symptom: Eine Plugin-Version in der CI unterscheidet sich von der auf deinem Rechner, wodurch die native Kompilierung wegen fehlender Symbole fehlschlägt.
Lösung: committe die Lockfile und installiere mit bun install --frozen-lockfile (oder npm ci). Pinne @capacitor/core, @capacitor/ios, @capacitor/android, und @capacitor/cli auf dieselbe Version. Versionskonflikte sind eine häufige Ursache für native Fehler; siehe Fix für Capacitor Version-Mismatch-Fehler.
Schritt 2: Die native Toolchain
Problem: Falsche Node-, JDK- oder Xcode-Version
Symptom: Unsupported class file major version, The engine "node" is incompatible, oder Xcode-Fehler bezüglich SDK Features.
Ursache: gehostete Runner-Images ändern sich, und Capacitor 8 hat feste Mindestanforderungen.
| Tool | Capacitor 8 Anforderung |
|---|---|
| Node.js | 22 oder neuer |
| JDK | 21 |
| Xcode | 26 oder neuer |
| iOS Deployment Target | 15.0 |
Android minSdkVersion / targetSdkVersion |
24 / 36 |
Lösung: Pinne jede Version explizit in der Pipeline, statt blind zu vertrauen latest:
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '26'
Pitfall: Build gegen eine Xcode-Version, die Apple nicht mehr unterstützt
Symptom: Der Upload schlägt mit der Meldung fehl, dass die App mit einem nicht unterstützten SDK gebaut wurde.
Ursache: Seit dem 28. April 2026 erfordert App Store Connect Xcode 26 und das iOS 26 SDK. Self-hosted Macs und ältere Runner-Images nutzen standardmäßig noch Xcode 16.
Lösung: wähle ein macos-26 Image oder installiere Xcode 26 auf deinen Runnern. Details unter Apples Xcode-26-Anforderung für Capacitor Apps.
Falle: Verwechslung von CocoaPods und SPM
Symptom: xcodebuild: error: 'App.xcworkspace' does not exist, oder Pods nicht gefunden.
Ursache: neue Capacitor 8 Projekte nutzen Swift Package Manager und bauen ios/App/App.xcodeproj. Ältere Projekte nutzen CocoaPods und bauen ios/App/App.xcworkspace danach pod install. Pipelines aus alten Tutorials setzen das Workspace voraus.
Lösung: Prüfe, welches du nutzt, und baue die richtige Datei. Bei einer Migration siehe So migrierst du deine Capacitor App zu SPM.
Falle: Android Plugin-Builds brechen nach Gradle-Upgrade
Symptom: Namespace not specified, package attribute is deprecated, oder Fehler eines Plugins build.gradle nach dem Update von Android Studio.
Lösung: fixiere das Android Gradle Plugin in android/build.gradle, upgrade auf einem Branch und aktualisiere zuerst die Plugins. Spezifische Fehler werden in Behebung von Capacitor Plugin-Buildfehlern mit AGP 9.
Schritt 3: Code Signing
Pitfall: iOS Signing funktioniert nur auf deinem Mac
Symptom: No signing certificate "iOS Distribution" found, No profiles for 'com.example.app' were found, oder errSecInternalComponent.
Ursache: Im Keychain deines Macs liegt das Zertifikat, und Xcode lädt die Profile für dich herunter. Ein CI-Runner hat beides nicht, und sein Keychain ist in einer nicht-interaktiven Session gesperrt.
Lösung: Importiere das Zertifikat in einen temporären, entsperrten Keychain und installiere das Profil dort, wo Xcode 16 und neuer danach sucht:
security create-keychain -p "$KEYCHAIN_PASSWORD" ci.keychain
security set-keychain-settings -lut 21600 ci.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" ci.keychain
security import dist.p12 -k ci.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" ci.keychain
security list-keychains -d user -s ci.keychain login.keychain
PROFILES="$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles"
mkdir -p "$PROFILES"
cp app.mobileprovision "$PROFILES/"
Nutze den Identity-Namen Apple Distribution, nicht den veralteten iOS Distribution. von fastlane setup_ci sowie match automatisiert das. Der Capgo Build nimmt Zertifikat und Profil als Umgebungsvariablen und erledigt die Keychain-Arbeit auf seinen eigenen Maschinen, sodass der Linux-Job den Keychain nie anfasst. security.
Pitfall: abgelaufene oder widerrufene Zertifikate
Symptom: Eine Pipeline, die ein Jahr lang lief, schlägt über Nacht fehl.
Ursache: Apple-Distributionszertifikate und Provisioning Profiles laufen nach einem Jahr ab. Wenn ein Teammitglied ein neues Zertifikat in Xcode erstellt, kann dies auch das Profil ungültig machen, das deine CI nutzt.
Lösung: Trage die Ablaufdaten in deinen Teamkalender ein, nutze ein gemeinsames Distributionszertifikat für die CI und prüfe den Status vor dem Build. bunx @capgo/cli@latest build prescan --platform ios prüft Zertifikatsablauf, Passwort und Profile-Pairing, bevor etwas hochgeladen wird.
Pitfall: Apple ID-Logins und Two-Factor-Prompts
Symptom: fastlane hängt fest und wartet auf einen 6-stelligen code.
Lösung: nutze einen App Store Connect API Key (.p8, Key ID, Issuer ID) anstatt Apple ID und Passwort. Dieser benötigt keine Zwei-Faktor-Authentifizierung und kann auf App Manager beschränkt werden. Falls die Authentifizierung trotz korrektem Key fehlschlägt, prüfe die Uhrzeit des Runners: Der Token wird mit der lokalen Zeit signiert, und Apple lehnt Tokens mit abweichender Zeitstempel ab.
Pitfall: base64-Secrets, die sich nicht dekodieren lassen
Symptom: MAC verification failed, invalid keystore format, oder base64: invalid input.
Ursache: Zeilenumbrüche beim Kopieren des Base64-Werts oder ein .p12 mit OpenSSL-3-Standardeinstellungen erstellt, die macOS nicht lesen kann.
Lösung: in einer Zeile kodieren und verwenden -legacy beim Erstellen eines .p12 mit OpenSSL 3:
base64 -i dist.p12 | tr -d '\n' > dist.p12.b64
openssl pkcs12 -export -legacy -inkey key.pem -in cert.pem -out dist.p12
Falle: ein verlorener Android-Keystore
Symptom: Du kannst ein Update nicht signieren, weil niemand den Keystore hat.
Lösung: with Play App Signing, the keystore is the upload key, and Play Console support can register a new one. Store the keystore in your CI secrets and in an offline backup. Never let it live only on one laptop. The Android Keystore Generator erstellt einen neuen, wenn du ganz von vorne beginnst.
Stufe 4: Pipeline-Design
Pitfall: Native Builds bei jedem Pull Request
Symptom: langsame PR-Checks und hohe Kosten für macOS-Minuten.
Ursache: iOS-Builds auf gehosteten macOS-Runnern sind in den meisten CI-Plänen die teuersten Minuten, obwohl die meisten Commits nur JavaScript betreffen.
Lösung: Führe Linting, Tests und den Web-Build bei jedem PR aus. Führe Native Builds bei Release-Tags oder Merges in mainFür PR-Previews solltest du das Web-Bundle in einen Capgo Channel pushen, anstatt ein Binary zu bauen, wie in Capgo beschrieben. CI/CD-Plattformen im Vergleich für Capacitor Apps.
Fehler: iOS und Android nacheinander bauen
Lösung: Nutze eine Matrix, damit beide Plattformen parallel bauen, und setze fail-fast: false damit ein iOS-Signing-Problem keinen erfolgreichen Android-Build abbricht.
strategy:
fail-fast: false
matrix:
platform: [ios, android]
Fehler: Kein Caching oder falsches Caching
Symptom: Jeder Build lädt Gradle-Dependencies und CocoaPods von Grund auf neu, oder ein Staging-Build liefert Produktions-Configs aus einem veralteten Cache.
Lösung: Cache ~/.gradle/caches, ~/.gradle/wrapper, und ios/App/Pods basierend auf den Lockfiles. Partitioniere Caches nach Umgebung. Mit Capgo Build kann der App-Build-Cache aufgeteilt werden mit --cache-key prod und --cache-key staging, oder übersprungen mit --no-cache für einen sauberen Build.
Pitfall: Monorepo-Pfade
Symptom: could not find capacitor.config oder Plugins fehlen im Native-Projekt.
Lösung: führe Capacitor Befehle aus dem App-Package aus und verweise Tools auf die gehoisteten node_modules. Der Capgo CLI akzeptiert --path und --node-modules dafür.
Schritt 5: Store-Submission
Fehlerquelle: Wiederverwendete Build-Nummern
Symptom: „The bundle version must be higher than the previously uploaded version“ unter iOS oder „Version code has already been used“ im Google Play Store.
Lösung: Generiere die Nummer in der CI. Mit fastlane liest du den neuesten TestFlight-Build aus und erhöhst ihn um eins. Bei Capgo Build ist dies der Standard: Es wird die neueste Build-Nummer von App Store Connect oder die höchste versionCode aus Google Play abgerufen und inkrementiert.
Fehlerquelle: Builds hängen in TestFlight fest
Symptom: Der Upload ist erfolgreich, aber Tester sehen den Build nicht.
Ursache: Fehlende Antwort zur Export-Compliance.
Lösung: einmal in ... deklarieren ios/App/App/Info.plist wenn du nur Standardverschlüsselung nutzt:
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
Falle: Ablehnung wegen Privacy Manifests
Symptom: E-Mail von Apple über fehlende API Erklärungen (ITMS-91053).
Lösung: füge ein PrivacyInfo.xcprivacy ...auf das App-Target und aktualisiere Plugins, die eigene Versionen mitliefern. Siehe dazu die Privacy-Manifest-Guide für iOS-Apps.
Falle: falscher Artefakt-Typ
Lösung: Google Play benötigt ein AAB (bundleRelease), keine APK. bundleRelease trotzdem erfolgreich ist, ohne eine release Signing-Config zu haben, und eine unsignierte AAB erzeugt, die der Play Store ablehnt. Konfiguriere daher signingConfigs.release in android/app/build.gradle Zuerst einmal: iOS benötigt einen App Store Export und keine Development- oder Ad-hoc-IPA. Überprüfe die Export-Methode in deinem Build-Schritt.
Schritt 6: Live Updates
Pitfall: Ausrollen eines live update, das einen neuen Native Build erfordert
Symptom: die App stürzt nach einem Over-the-Air Update ab, weil eine Plugin-Methode aufgerufen wird, die in der installierten Binary nicht existiert.
Ursache: das Web-Bundle hängt von einer Plugin-Version ab, die neuer ist als die in der App auf den Geräten der User kompilierte Version.
Lösung: überlass die Entscheidung der Pipeline. build needed gibt 0 zurück, wenn die nativen Abhängigkeiten mit dem Channel übereinstimmen, und 1, wenn ein neues Binary benötigt wird:
if bunx @capgo/cli@latest build needed com.example.app --channel production; then
bunx @capgo/cli@latest bundle upload com.example.app --channel production
else
bunx cap sync
bunx @capgo/cli@latest build request com.example.app --platform ios
bunx @capgo/cli@latest build request com.example.app --platform android
fi
Erzwinge zudem den nativen Pfad, wenn Dateien unter ios/, android/, oder capacitor.config.* sich ändern. Das vollständige Pattern findest du in Automatische Wahl zwischen live update oder Native Build, und die Kompatibilitätsregeln stehen in nativer Kompatibilität.
Die Lösung, die die meisten Pitfalls beseitigt
Wenn du nur eine Sache ändern willst: Lagere die iOS-Kompilierung und das Signing aus deinen CI-Runnern aus. Keychain-Setup, Xcode-Upgrades, macOS-Kosten und die Profil-Installation fallen aus deiner Pipeline weg, sobald ein Linux-Job das vorbereitete Projekt an Capgo Build:
bun install --frozen-lockfile && bun run build
bunx cap sync ios
bunx @capgo/cli@latest build request com.example.app --platform ios --build-mode release
Die Pitfalls in den Stufen 1, 5 und 6 gelten weiterhin, da sie dein Projekt betreffen, nicht den Runner. Mehr zum Debugging fehlerhafter Jobs findest du unter Build-Fehler in Capacitor CI/CD-Pipelines beheben.
Kurzübersicht
| Fehler | Fallstrick | Abschnitt |
|---|---|---|
| Alte UI in einem neuen Build | cap sync vor dem Web-Build |
Phase 1 |
No profiles for ... were found |
Profil nicht installiert oder fehlerhaft | Phase 3 |
errSecInternalComponent |
Keychain gesperrt | Phase 3 |
MAC verification failed |
Falsches Passwort oder OpenSSL 3 .p12 |
Stufe 3 |
Unsupported class file major version |
Falsches JDK | Stufe 2 |
| SDK beim Upload zu alt | Xcode älter als Version 26 | Stufe 2 |
| Bundle-Version muss höher sein | Build-Nummer bereits verwendet | Stufe 5 |
| Version code bereits vergeben | Reused versionCode |
Stufe 5 |
| Crash nach live update | Native Änderung per OTA ausgeliefert | Stufe 6 |