◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 36 days left. If your app calls it, check the migration target●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 36 days left. If your app calls it, check the migration target●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Business
⟐ Business/2026-10-25Beginner

It Looks Opaque, but App Store Connect Says the Icon Has an Alpha Channel — Checking the PNG Before You Upload

ITMS-90717 (Invalid large app icon) can fire on an icon that shows no transparency at all, because an alpha channel is enough. I built six sample PNGs to tell the cases apart, and share a stdlib-only checker and a flatten step.

App Store Connect16ITMS-90717app iconindie developer48Rork600troubleshooting77

I once laid out six 1024px PNGs side by side to find out which ones App Store Connect would turn away. One had a filled background. One had rounded corners. One was grayscale, and one had its palette reduced. Looking at them, none of them was see-through.

Yet the validation can still come back with this:

ITMS-90717: Invalid large app icon. The large app icon in the asset catalog in "___.app" can't be transparent or contain an alpha channel.

"Can't be transparent" and "can't contain an alpha channel" sound like the same rule. They aren't. This post checks the difference on sample files and ends with a check you can run before every upload.

The thing I'd like you to take away first: an icon that looks opaque tells you nothing about whether it is safe.

What the symptom looks like

ITMS-90717 shows up during validation when you upload a build, or in the message that follows processing. The core phrase is "can't be transparent or contain an alpha channel."

Other problems get mistaken for it. This table helps you place your own message.

What you seeCovered here?Look at first
Invalid large app icon … can't be transparent or contain an alpha channelYesThe 1024px icon PNG (is there an alpha channel?)
Build stuck on "Processing"Different postProcessing queue, build number, email
Changed the icon but the old one still showsDifferent symptomCaches, assets that weren't replaced
Android notification icon is a white squareDifferent symptomHow the monochrome notification icon is made

If you're unsure whether it's the "Processing" case, start with this walkthrough for builds stuck in processing.

I had misread the rule

Expo's documentation says the iOS icon should fill the whole square, with no rounded corners and no transparent pixels. The OS applies its own mask, so you don't round anything yourself.

For a long time I read that as "it's fine if it doesn't look see-through." That was the misreading. The error message names two conditions on purpose: not transparent, and no alpha channel.

If every pixel is fully opaque but the file is still RGBA, the alpha channel exists. You can't tell by eye.

What I measured: six sample files

I made six 1024px samples and read each PNG's IHDR header to see how it stores color. Python 3's standard library was enough.

SampleLooks likeCheck result
RGB (no alpha)OpaquePass
RGBA, every pixel opaqueOpaque (identical to the RGB one)Fail: has an alpha channel
RGBA, rounded corners made transparentCorners show throughFail: has an alpha channel
Grayscale + alphaOpaqueFail: has an alpha channel
Palette PNG with transparencyLooks opaqueFail: tRNS present
RGB, but 1000×1000OpaqueFail: wrong size

The second row is the point. The minimum and maximum of the alpha channel were both 255, so no pixel is see-through. The file is still RGBA, and it still carries the channel.

One limit on all this: the check only tells you what is inside the file. I did not submit these six to App Store Connect to see whether each is accepted. The Apple-side verdict rests on the wording of the error and on public forum reports.

The order I'd check causes in

  1. Is the exported PNG RGBA? Many design tools export RGBA by default. A painted background doesn't remove the channel.
  2. Did you bake rounded corners in? The corners become transparent. Hand over a plain square and let the OS do the rounding.
  3. Is it exactly 1024×1024? Off-by-a-little sizes like 1000px or 1024×1023 turn up late.
  4. Are you using an Icon Composer .icon? Apple's developer forums have several reports from the Xcode 26 beta period of PNGs generated from Icon Composer icons containing alpha. One poster narrowed it to a group with Blur on and an SVG near the canvas edge. An Apple engineer said they were investigating, and reports differed from one build to the next.
  5. Is the build stale? If you fixed the image and get the same error, suspect old assets.

Steps 1 to 3 can be settled with one file. Step 4 depends on the tool you export from.

The checker

It takes one or many files and reads only the PNG header, so it never opens the image.

#!/usr/bin/env python3
"""Check App Store icon PNGs before upload (standard library only).
Usage: python3 icon_alpha_check.py icon.png [more.png ...]
Exit code: 0 = all pass / 1 = at least one needs work
"""
import struct, sys
 
COLOR = {0: "gray", 2: "RGB", 3: "palette", 4: "gray+alpha", 6: "RGBA"}
 
def inspect(path):
    with open(path, "rb") as f:
        data = f.read()
    if data[:8] != b"\x89PNG\r\n\x1a\n":
        return None, ["not a PNG"]
    pos, width, height, ctype, has_trns = 8, 0, 0, -1, False
    while pos + 8 <= len(data):
        length, kind = struct.unpack(">I4s", data[pos:pos + 8])
        body = data[pos + 8:pos + 8 + length]
        if kind == b"IHDR":
            width, height, _depth, ctype = struct.unpack(">IIBB", body[:10])
        elif kind == b"tRNS":
            has_trns = True
        pos += 12 + length
    problems = []
    if (width, height) != (1024, 1024):
        problems.append(f"size is {width}x{height} (need 1024x1024)")
    if ctype in (4, 6):
        problems.append(f"has an alpha channel ({COLOR[ctype]})")
    if ctype == 3 and has_trns:
        problems.append("palette PNG with transparency (tRNS)")
    return (width, height, COLOR.get(ctype, str(ctype))), problems
 
def main(paths):
    bad = 0
    for p in paths:
        info, problems = inspect(p)
        if problems:
            bad += 1
            print(f"NG  {p}: " + " / ".join(problems))
        else:
            print(f"OK  {p}: {info[0]}x{info[1]} {info[2]}")
    return 1 if bad else 0
 
if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]) if len(sys.argv) > 1 else 2)

On my six samples it printed:

OK  a_rgb.png: 1024x1024 RGB
NG  b_rgba_opaque.png: has an alpha channel (RGBA)
NG  c_rgba_rounded.png: has an alpha channel (RGBA)
NG  d_la.png: has an alpha channel (gray+alpha)
NG  e_palette_trns.png: palette PNG with transparency (tRNS)
NG  f_rgb_1000.png: size is 1000x1000 (need 1024x1024)

Because it returns an exit code, it drops straight into a pre-submit step. One NG means no upload — that's the line I draw.

The fix: remove alpha and save as RGB

Decide what color fills the transparent areas first. An icon with baked-in rounded corners has transparent corners, so they need a background color.

#!/usr/bin/env python3
"""Write an RGB PNG with the alpha channel removed.
Usage: python3 icon_flatten.py in.png out.png [#RRGGBB]
"""
import sys
from PIL import Image  # pip install pillow
 
src, dst = sys.argv[1], sys.argv[2]
bg = sys.argv[3] if len(sys.argv) > 3 else "#FFFFFF"
bg_rgb = tuple(int(bg.lstrip("#")[i:i + 2], 16) for i in (0, 2, 4))
im = Image.open(src).convert("RGBA")
flat = Image.new("RGB", im.size, bg_rgb)
flat.paste(im, mask=im.getchannel("A"))
flat.save(dst, "PNG")
print(f"{dst}: {flat.mode} {flat.size}")

Running the opaque RGBA sample through it gave an image with zero pixel difference from the original RGB sample — the difference bounding box came back empty (None). The picture is unchanged and only the channel is gone.

The rounded-corner sample comes out as a plain square with the corners filled. Since the OS rounds the corners itself, nothing visible is lost.

If you use ImageMagick, one line does the same:

convert icon.png -background "#FAFAFA" -alpha remove -alpha off PNG24:icon_fixed.png

I tripped on one thing here. My first run omitted PNG24:. The alpha was gone, but the output was a palette PNG (indexed color, 256 colors or fewer). On an icon with gradients that can make the colors look rough. With PNG24: the output was a plain RGB PNG.

If it still fails

If the script says OK and the same error keeps coming, check these in order:

  • Did you actually upload the fixed file, or the old build? Bump the build number and upload again.
  • Does the asset catalog hold more than one 1024px PNG? The older one may be the one being checked.
  • If you use an Icon Composer .icon, the forum reports mention turning Blur off, going back to a plain PNG, or stripping alpha from the exported PNG. Each gives up some of the visual effect, and reports varied by build, so it's worth checking whether the latest Xcode still reproduces it before changing anything.

I haven't verified how Rork's icon settings or export behave for this case, so I'm not describing them here. Please check the screens and names in your own environment.

Making it a habit

Judge transparency with your eyes, and the alpha channel with the bytes. That's the line I keep before every submission.

As an indie developer, I find the day I touch the icon and the day I submit are often far apart. An image exported a few days earlier gets a glance — "looks fine" — on submit day. I'd rather hand that glance to a script that takes seconds.

After the icon, the next thing worth checking before submit is whether a secret value has slipped into the app. I wrote up how EAS secrets and public variables differ along two axes, if you'd like to read on.

For today, run one 1024px PNG you already have through icon_alpha_check.py. That's all.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⟐ Business2026-07-19
Handing Over an App — What an App Store Transfer Carries, and What You Rebuild
A practical look at App Store Connect app transfers when selling or moving an app: the eligibility criteria, what carries over (ratings, subscribers, iCloud data), what you rebuild (TestFlight, APNs, merchant IDs), and the three places users actually feel the change.
⟐ Business2026-10-24
Google Play's Extension Ends November 1 — Count Backwards from Your Submit Day
The target API 36 extension on Google Play ends November 1. Instead of counting to the deadline itself, count back from the last day you can submit — with a small script that does the date math.
⟐ Business2026-10-08
Before You Build a Client's App in Rork, Decide Who Owns the Store Account
When you hand a Rork-built app to someone else, the first decision is which developer account publishes it. A walk through Apple's and Google's official transfer rules: what must be true before a transfer, what carries over, what disappears, plus a small script that lists the settings in your project tied to your own name.