La mayoría de los fallos de CI/CD en Capacitor vienen de la misma lista corta: signing de code para iOS, herramientas de macOS faltantes o desactualizadas, un web build que nunca llegó al proyecto nativo, build numbers reutilizados y requisitos de la store que solo fallan al hacer upload. Cada uno tiene una causa conocida y una solución que puedes aplicar una vez. Esta guía agrupa los errores por etapa del pipeline, con el error que verás, por qué ocurre y qué cambiar.
Si estás configurando un pipeline desde cero, lee Configuración de CI/CD para apps de Capacitor primero, y luego usa esta lista para reforzarlo.
Fase 1: construir la capa web
Error común: la app nativa incluye un build web antiguo
Síntoma: El CI pasa y la app se instala, pero muestra la UI de ayer.
Causa: cap sync copia lo que haya en webDir en ese momento. Si el pipeline se ejecuta cap sync antes del build web, o el build web escribe en una carpeta distinta a webDir en capacitor.config.ts, el proyecto nativo se queda con archivos obsoletos.
Solución: ejecuta siempre los pasos en este orden y falla si la carpeta de salida está vacía.
bun install --frozen-lockfile
bun run build
test -f dist/index.html || { echo "web build missing"; exit 1; }
bunx cap sync
Asegúrate de que webDir coincide con el output de tu bundler (dist si usas Vite, www para Angular con Ionic, build para algunas configuraciones de React).
Error común: dejar la URL del dev server en la config
Síntoma: la build de release muestra una pantalla en blanco o intenta cargar http://192.168.x.x:5173.
Causa: server.url en capacitor.config.ts se configuró para live reload y se hizo commit.
Solución: nunca hagas commit de server.url. Léelo desde una variable de entorno que el CI nunca defina:
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
Error común: variables de entorno incrustadas en el momento equivocado
Síntoma: la app de producción se conecta al API de staging.
Causa: Vite, webpack y Angular incluyen variables de entorno en línea durante el build. El valor presente cuando bun run build se ejecute es el que quedará en el binario y en cualquier live update generado con ese mismo job.
Solución: define las variables según el entorno antes del build web, por cada job, y genera bundles separados para staging y producción. No intentes cambiarlas después cap sync.
Error común: drift en el lockfile
Síntoma: la versión de un plugin en CI es distinta a la de tu máquina y la compilación nativa falla por símbolos faltantes.
Solución: haz commit del lockfile e instala con bun install --frozen-lockfile (o npm ci). Fija @capacitor/core, @capacitor/ios, @capacitor/android, y @capacitor/cli la misma versión. Las versiones desajustadas causan errores nativos frecuentes; consulta Soluciona errores de desajuste de versión en Capacitor.
Etapa 2: El toolchain nativo
Error común: versión incorrecta de Node, JDK o Xcode
Síntoma: Unsupported class file major version, The engine "node" is incompatible, o errores de Xcode con funciones de SDK.
Causa: cambian las imágenes de los runners alojados, y Capacitor 8 tiene mínimos estrictos.
| Herramienta | Requisito de Capacitor 8 |
|---|---|
| Node.js | 22 o superior |
| JDK | 21 |
| Xcode | 26 o superior |
| Target de despliegue de iOS | 15.0 |
Android minSdkVersion / targetSdkVersion |
24 / 36 |
Solución: fijar cada versión explícitamente en el pipeline en lugar de confiar 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'
Error: compilar con una versión de Xcode que Apple ya no acepta
Síntoma: el upload falla con un mensaje indicando que la app se compiló con un SDK no compatible.
Causa: desde el 28 de abril de 2026, App Store Connect requiere Xcode 26 y el SDK de iOS 26. Los Macs self-hosted y las imágenes de runner antiguas siguen usando Xcode 16 por defecto.
Solución: selecciona una macos-26 imagen o instala Xcode 26 en tus runners. Detalles en el requisito de Xcode 26 de Apple para apps Capacitor.
Error común: confusión entre CocoaPods y SPM
Síntoma: xcodebuild: error: 'App.xcworkspace' does not exist, o no se encuentran los pods.
Causa: los nuevos proyectos de Capacitor 8 usan Swift Package Manager y compilan ios/App/App.xcodeproj. Los proyectos antiguos usan CocoaPods y compilan ios/App/App.xcworkspace después de pod install. Los pipelines copiados de tutoriales viejos asumen el workspace.
Solución: revisa cuál usa tu proyecto y compila el archivo correcto. Si estás migrando, consulta Cómo migrar tu app de Capacitor a SPM.
Error común: los builds de plugins de Android fallan tras actualizar Gradle
Síntoma: Namespace not specified, package attribute is deprecated, o errores de un plugin build.gradle tras actualizar Android Studio.
Solución: fija el Android Gradle Plugin en android/build.gradle, sube a una rama y actualiza plugins primero. Ver errores específicos en Corrige errores de build del plugin Capacitor con AGP 9.
Etapa 3: firma de Code
Error común: el signing de iOS solo funciona en tu Mac
Síntoma: No signing certificate "iOS Distribution" found, No profiles for 'com.example.app' were found, o errSecInternalComponent.
Causa: el keychain de tu Mac contiene el certificado y Xcode descarga los profiles por ti. Un runner de CI no tiene ninguno y su keychain está bloqueado en una sesión no interactiva.
Solución: importa el certificado en un keychain temporal desbloqueado e instala el profile donde Xcode 16 y versiones posteriores lo buscan:
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/"
Usa el nombre de la identidad Apple Distribution, no el legacy iOS Distribution. El de fastlane setup_ci además de match automatiza esto. Capgo Build toma el certificado y el profile como variables de entorno y gestiona el keychain en sus propias máquinas, así que el job de Linux nunca toca security.
Error común: certificados expirados o revocados
Síntoma: un pipeline que funcionó un año falla de la noche a la mañana.
Causa: Los certificados de distribución de Apple y los provisioning profiles caducan al año. Si un compañero crea un nuevo certificado en Xcode, también puede invalidar el profile que usa tu CI.
Solución: añade las fechas de vencimiento al calendario de tu equipo, usa un certificado de distribución compartido para el CI y verifica todo antes de hacer el build. bunx @capgo/cli@latest build prescan --platform ios verifica la expiración del certificado, la contraseña y el emparejamiento del perfil antes de subir nada.
Error común: inicios de sesión de Apple ID y prompts de doble factor
Síntoma: fastlane se queda colgado esperando un code de 6 dígitos.
Solución: usa una clave API de App Store Connect (.p8, key ID, issuer ID) en lugar de un Apple ID y contraseña. No requiere autenticación de dos factores y puede limitarse al rol de App Manager. Si la autenticación falla con una clave correcta, revisa el reloj del runner: el token se firma con la hora local y Apple rechaza tokens con un timestamp desfasado.
Error común: secretos en base64 que no se decodifican
Síntoma: MAC verification failed, invalid keystore format, o base64: invalid input.
Causa: saltos de línea al copiar el valor base64, o un .p12 creado con los valores predeterminados de OpenSSL 3 que macOS no puede leer.
Solución: codificar en una sola línea, y usar -legacy al crear un .p12 con 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
Error común: perder el keystore de Android
Síntoma: no puedes firmar una actualización porque nadie tiene el keystore.
Solución: Con Play App Signing, el keystore es la clave de carga, y el soporte de Play Console puede registrar una nueva. Guarda el keystore en los secrets de tu CI y en un backup offline. Nunca dejes que solo esté en un portátil. El Generador de keystore para Android crea uno nuevo si empiezas de cero.
Etapa 4: Diseño del pipeline
Error común: builds nativos en cada pull request
Síntoma: checks de PR lentos y una factura enorme de minutos en macOS.
Causa: Los builds de iOS en runners de macOS alojados son los minutos más caros en la mayoría de planes de CI, y la mayoría de los commits solo tocan JavaScript.
Solución: ejecuta lint, tests y el build web en cada PR. Ejecuta builds nativos en tags de release o merges a main. Para previews de PR, envía el bundle web a un canal de Capgo en lugar de compilar un binario, como se describe en Comparativa de plataformas CI/CD para apps Capacitor.
Error común: compilar iOS y Android de forma secuencial
Solución: usa una matriz para compilar ambas plataformas en paralelo, y configura fail-fast: false para que un error de firma en iOS no cancele un build de Android correcto.
strategy:
fail-fast: false
matrix:
platform: [ios, android]
Error común: sin caché, o con la caché incorrecta
Síntoma: cada build descarga las dependencias de Gradle y CocoaPods desde cero, o un build de staging incluye config de producción desde una caché obsoleta.
Solución: caché ~/.gradle/caches, ~/.gradle/wrapper, y ios/App/Pods basada en los lockfiles. Divide las cachés por entorno. Con Capgo Build, la caché de build por app se puede dividir con --cache-key prod y --cache-key staging, o se omite con --no-cache para un build limpio.
Error común: rutas de monorepo
Síntoma: could not find capacitor.config o plugins que faltan en el proyecto nativo.
Solución: ejecuta comandos de Capacitor desde el paquete de la app y apunta las herramientas a hoisted node_modules. El Capgo CLI acepta --path y --node-modules para esto.
Etapa 5: Envío a la tienda
Error común: reutilizar números de build
Síntoma: “The bundle version must be higher than the previously uploaded version” en iOS, o “Version code has already been used” en Google Play.
Solución: genera el número en tu CI. Con fastlane, lee el último build de TestFlight y súmale uno. Con Capgo Build, esto es lo predeterminado: obtiene el último número de build de App Store Connect o el más alto versionCode de Google Play y lo incrementa.
Error común: builds trabados en TestFlight
Síntoma: el upload es exitoso pero los testers nunca ven el build.
Causa: missing export compliance answer.
Solución: decláralo una vez en ios/App/App/Info.plist si solo usas cifrado estándar:
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
Error común: rechazos por privacy manifest
Síntoma: email de Apple sobre declaraciones de API de motivo requerido que faltan (ITMS-91053).
Solución: añade un PrivacyInfo.xcprivacy al target de la app y actualiza los plugins que incluyan el suyo. Consulta la guía de privacy manifest para apps de iOS.
Error común: tipo de artifact incorrecto
Solución: Google Play requiere un AAB (bundleRelease), no un APK. bundleRelease sigue teniendo éxito sin una release configuración de firma y genera un AAB sin firmar que Play rechaza, así que configura signingConfigs.release en android/app/build.gradle primero. iOS requiere un export para App Store, no un IPA de desarrollo o ad hoc. Revisa el método de exportación en tu build step.
Etapa 6: Live updates
Error común: lanzar un live update que requiere un nuevo Native Build
Síntoma: tras una actualización OTA, la app falla al llamar a un método de un plugin que no existe en el binario instalado.
Causa: el bundle web depende de una versión de plugin más reciente que la que se compiló en la app de los dispositivos de los usuarios.
Solución: deja que el pipeline decida. build needed devuelve 0 cuando las dependencias nativas coinciden con lo que está activo en el channel y 1 cuando se requiere un nuevo binario:
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
También fuerza la ruta nativa cuando los archivos en ios/, android/, o capacitor.config.* cambian. El patrón completo está en Elige automáticamente live update o Native Build, y las reglas de compatibilidad están en compatibilidad nativa.
La solución que elimina la mayoría de los errores
Si solo vas a cambiar una cosa, saca la compilación y la firma de iOS de tus runners de CI. La configuración de Keychain, las actualizaciones de Xcode, los costes de macOS y la instalación de perfiles desaparecen de tu pipeline cuando un job de Linux le pasa el proyecto preparado a Build de Capgo:
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
Los errores en las etapas 1, 5 y 6 siguen presentes, porque tienen que ver con tu proyecto, no con el runner. Para más info sobre cómo debuggear jobs fallidos, consulta Cómo solucionar fallos de build en pipelines de CI/CD de Capacitor.
Referencia rápida
| Error | Error común | Sección |
|---|---|---|
| UI antigua en un nuevo build | cap sync antes del build web |
Etapa 1 |
No profiles for ... were found |
Perfil no instalado o incompatible | Etapa 3 |
errSecInternalComponent |
Keychain bloqueado | Etapa 3 |
MAC verification failed |
Contraseña incorrecta u OpenSSL 3 .p12 |
Etapa 3 |
Unsupported class file major version |
JDK incorrecto | Etapa 2 |
| SDK demasiado antiguo al subir | Xcode anterior a la versión 26 | Etapa 2 |
| La versión del bundle debe ser mayor | Número de build reutilizado | Etapa 5 |
| La versión code ya está en uso | Reutilizado versionCode |
Etapa 5 |
| Crash tras live update | Cambio nativo enviado vía OTA | Etapa 6 |