DEV Community

Jackson
Jackson

Posted on

The iOS certificate problem nobody has actually solved

If you have ever set up iOS code signing for a CI pipeline, you already know the feeling. Everything works for months, then one day a build fails with a certificate error, and you spend the next two hours relearning a system you thought you understood.

I want to write about why that keeps happening, because it is not a "you set it up wrong" problem. It is a problem the most popular tool in this space has openly said it has not solved.

The admission

fastlane is the standard way most teams manage iOS signing. It is free, open source, and has been around since 2015. Its own maintainers have said, on a public GitHub discussion, that certificate lifecycle management is one of the few things fastlane still has not figured out:

"The problem of expiring certificates to me is one of the few fundamental signing features that fastlane has not yet solved... we all face again and again... I really hope we can put this issue to bed soon!"

That is not a random forum comment. That is a maintainer, on the project's own GitHub, describing a problem that keeps coming back for everyone who uses the tool. If you search fastlane's issue tracker, you will find real, specific reports of what that looks like in practice. One developer described fastlane match hanging indefinitely on "Installing provisioning profile," and said outright: "I accidentally tried waiting for 2.5 hours." Another reported an expired WWDR intermediate certificate silently breaking their local keychain with no clear error pointing at the actual cause.

And when match genuinely gets stuck, the documented recovery path is match nuke, which deletes every certificate and profile of the affected type for your entire team, not just yours. One developer's reaction to that in the same thread sums it up: "does nuking remove all certificates & profiles... including other people's... in your iOS team?" Yes. It does.

I am not writing this to pick on fastlane. It is a genuinely useful tool and a lot of serious iOS shops run on it successfully. I am writing this because if the tool built specifically to solve this problem says the problem is not solved, that tells you something real about how the underlying system works, not about who is using it wrong.

Why the certificates themselves are the trap

Apple caps how many active Distribution certificates you can have on an account. In practice this is usually two or three. That sounds generous until you see what actually exhausts it.

If your CI pipeline is set up to auto-provision (generate a fresh certificate on every run instead of reusing one you already created), you will hit that cap surprisingly fast. Bitrise's own documentation for their automatic provisioning steps says this plainly: without a certificate you pre-uploaded yourself, "steps with automatic provisioning will generate one on the fly every time." Run that pipeline a few dozen times and you can burn through your entire certificate allowance without ever manually creating one.

When you do hit the cap, the error from Apple's own API is not exactly friendly:

"Could not create another Distribution certificate,
reached the maximum number of available Distribution certificates."
Enter fullscreen mode Exit fullscreen mode

That message tells you what happened. It does not tell you which of your existing certificates is safe to revoke, or which apps depend on which certificate. If you have more than one app signed with certificates from the same account, you now have to go figure that out manually before you can unblock yourself, usually while a release is waiting.

The errors that are not your fault

A few other things I have hit, or seen well documented elsewhere, that are worth knowing before they happen to you at 11pm the night before a submission:

A bare 500 on profile creation. UNEXPECTED_ERROR on a POST to the provisioning profiles endpoint is a real, recurring error reported directly on Apple's own developer forums. Sometimes it resolves on a plain retry with zero changes to your config. If you see this, try again before you start second-guessing your setup.

The wrong kind of API key. App Store Connect API keys come in two flavors: Individual and Team. Provisioning-related endpoints specifically require a Team key. If you are using an Individual key, you will get a 403 on exactly those endpoints while everything else you try with that same key works fine, which makes it a lot more confusing to diagnose than a blanket permission error would be.

Enterprise vs. App Store account type is invisible to the API. Apple's own documentation admits the API cannot automatically tell these two account types apart, so if you are generating certificates programmatically for an Enterprise (in-house distribution) account, you need to set the in_house flag manually. Nothing will warn you if you get this wrong until later.

Apple can change the API out from under you with no warning. In 2025, Apple silently removed an undocumented parameter, templateName, from the App Store Connect API. That parameter was load-bearing for fastlane's automated provisioning-profile creation, and its removal broke automated signing industry-wide with no migration path and no changelog entry anyone could point to in advance. There is a real postmortem about it if you want the full story (search "Sourcetoad Apple templateName fastlane"), and the related GitHub issue was still open the last time I checked. This is the part that is genuinely hard to defend against: the tool can be correct today and wrong next month because Apple changed something nobody announced.

What actually helps

None of this means give up and do everything by hand. A few things that make a real, measurable difference:

Check your certificate count before you create a new one, not after you get the error. A simple GET on the certificates endpoint tells you exactly how many of your two or three slots are already in use, so you can decide whether to reuse one or revoke an old one before you are blocked mid-release.

Never auto-provision on every CI run. Generate your signing certificate and provisioning profile once, store them (encrypted) somewhere your pipeline can reach, and reuse them. This alone avoids most of the cap-exhaustion problem entirely.

Build retry logic around the specific error codes above, not a generic catch-all. A 500 on profile creation deserves a retry with backoff. A 403 on provisioning specifically, when your key works everywhere else, deserves a message that says "check whether this is a Team key," not "permission denied."

Keep a place to watch for expiry before it becomes an outage. A scheduled check that looks at your certificate and profile expiration dates and pings you a few weeks out is a small thing to build and it turns a surprise failure into a calendar reminder.

I ended up building most of the last two points directly into my own pipeline (Macless, macless.dev), because I kept hitting exactly this set of problems while shipping my own app without owning a Mac, and got tired of relearning the same lessons every few months. If you are dealing with this on your own setup right now, I hope at least the specific error messages above save you the hour I spent figuring out what they meant the first time.

Top comments (0)