fastlane talks to Apple's developer services two different ways. The modern path uses an App Store Connect API key, which is a signed JWT generated fresh on every run. The older path, called spaceship, logs in with an Apple ID and password the way a browser does, and then reuses the session cookie Apple hands back. This message means that cookie is dead.
Nothing you did caused it. Apple expires these sessions on its own clock, and the clock is short. The thing that makes it feel arbitrary is that the failure lands on a pipeline that has not been touched in weeks, which is exactly the pipeline that has been quietly riding one cookie the whole time.
Two-factor authentication is what turned this from an annoyance into a recurring outage. Once 2FA is enforced on the account, and it is enforced on every account now, the session is bound to a device-trust cookie that Apple issues after you type a six-digit code. That trust lasts roughly a month in practice. A build server cannot type a six-digit code, so when the trust lapses the job simply fails.
The stopgap: regenerate the session
If you need the build green in the next five minutes, generate a new session on a machine where you can receive the 2FA code, and put the result in CI. This is worth knowing, but treat it as a patch rather than a fix.
fastlane spaceauth -u you@example.com
The command prompts for the password and the verification code, then prints a long block of text starting with ---. That entire block, newlines included, is the value of FASTLANE_SESSION. Copy all of it.
Expect to redo this every few weeks. Teams that go this route end up with a calendar reminder, a runbook, and a release that slips because the one person who knows the runbook is on holiday. That is the argument for the next section, not the elegance of the API.
The actual fix: an App Store Connect API key
An API key is issued from App Store Connect under Users and Access, Integrations. You get a key ID, an issuer ID, and a .p8 private key file that Apple lets you download exactly once. fastlane signs a short-lived token with that key on every call, so there is no session to expire and no six-digit code to type.
# Fastfile
lane :release do
api_key = app_store_connect_api_key(
key_id: ENV["ASC_KEY_ID"],
issuer_id: ENV["ASC_ISSUER_ID"],
key_content: ENV["ASC_KEY_P8"], # the .p8 contents, or base64 with is_key_content_base64
in_house: false
)
match(type: "appstore", api_key: api_key, readonly: true)
build_app(scheme: "MyApp")
upload_to_testflight(api_key: api_key)
endThe actions that accept an api_key parameter include match, sigh, cert, pilot, deliver, produce, and precheck. Passing it explicitly is better than relying on the lane context, because an action that does not receive the key falls back to Apple ID authentication and you are back here without an obvious reason why.
Checking your work
Test the credential on its own before you test it inside a twenty-minute build. Both of these are cheap read-only calls that fail fast if the key is wrong.
# Does the key authenticate at all? fastlane run app_store_connect_api_key \ key_id:"$ASC_KEY_ID" \ issuer_id:"$ASC_ISSUER_ID" \ key_filepath:"$ASC_KEY_PATH" # Does it have the access the lane needs? fastlane pilot list --api_key_path asc_key.json
If authentication succeeds but a later step complains about permissions, the key's role is too narrow. Keys are assigned a role when they are created, and a Developer-role key cannot do everything an Admin-role key can. Check the role in App Store Connect rather than regenerating the key.
Related messages
- "Invalid username and password combination." The credentials are genuinely wrong, or the account needs a password reset at appleid.apple.com. Changing an Apple ID password invalidates every existing session immediately, which is a common trigger for a fleet of pipelines failing at once.
- "Your Apple ID has been locked for security reasons." Repeated failed logins from a runner will do this. Unlock at iforgot.apple.com, then move to an API key before it happens again.
- "Your session cookie has been expired. Please log in again." A variant emitted from a different point in spaceship. Same cause, same fix.
- Portal errors that look unrelated but are really an unauthenticated session, such as No profiles for 'com.example.app' were found when match cannot reach Apple to fetch anything.
An app-specific password is worth mentioning because it is frequently suggested and frequently does not help. It authenticates uploads to TestFlight through the transporter tooling, and nothing else. It has no authority over certificates, identifiers, or profiles, so a lane that fails during fastlane match will keep failing with one set.