Skip to content

fix(cpp): scope the GenAI Android AAR cache by version and never cache a failed download - #960

Draft
sheetalarkadam wants to merge 2 commits into
mainfrom
fix/genai-aar-cache-staleness
Draft

fix(cpp): scope the GenAI Android AAR cache by version and never cache a failed download#960
sheetalarkadam wants to merge 2 commits into
mainfrom
fix/genai-aar-cache-staleness

Conversation

@sheetalarkadam

Copy link
Copy Markdown

Problem

Two independent caching defects in the Android GenAI AAR path. Both produce a build that silently disagrees with the pinned version, and both are invisible in CI.

1. Stale reuse across version bumps

The archive was stored as onnxruntime-genai-android.aar and extracted to a single genai-android-aar/ directory — neither includes the version. Both steps are guarded by EXISTS:

set(_GENAI_AAR_FILE "${_GENAI_AAR_DIR}/onnxruntime-genai-android.aar")
if(NOT EXISTS "${_GENAI_AAR_FILE}")   # bump -> still exists -> skipped
if(NOT EXISTS "${_GENAI_AAR_DIR}/jni") # bump -> still exists -> skipped

So bumping ORT_GENAI_VERSION reuses whichever AAR was downloaded first, while logging the new version. A warm build directory produces binaries from the old release.

This is not hypothetical. v0.14.1 has no Android AAR attached to its GitHub release, so a machine that had previously fetched 0.14.0 kept building against 0.14.0 while reporting 0.14.1 — and the missing release went unnoticed.

CI never sees this: every run starts from a fresh binary directory. Developers hit it on every bump.

2. Poisoned cache entry on a failed download

file(DOWNLOAD) writes its destination even when the transfer fails. Verified against a real unpublished release:

STATUS = 22;"HTTP response code said error"
file exists, size = 0 bytes

The FATAL_ERROR left that 0-byte file in place, so the next configure saw EXISTS, skipped the download, and failed inside ARCHIVE_EXTRACT instead — permanently, until someone deleted it by hand. The second failure gives no hint that the real cause was a missing release.

Fix

  • Scope both the archive and the extracted tree by version, so a bump fetches its own copy and cannot collide.
  • Download to a temporary path and file(RENAME) only on success, so a failed fetch leaves no cache entry and the next attempt genuinely retries.
  • Name the version and URL in the error, and point at the likely cause — a missing Android AAR on an otherwise healthy release is the common failure.

Also stages libmat.so, which GenAI 0.15.0+ splits out of libonnxruntime-genai.so and records as a DT_NEEDED. It is Android-only; the desktop package is still monolithic, so the copy is guarded on existence and 0.14.x is unaffected.

Verification

  • HTTP 404 leaves no cache entry, and a retry re-attempts rather than reusing garbage.
  • 0.15.0 downloads (21,556,055 bytes), hits cache on re-run, and lands in a directory separate from 0.14.0 (38.19 MB vs 20.56 MB side by side — under the old scheme the second fetch was skipped entirely).
  • Clean Android arm64 build against the GitHub AAR: all four .so staged and the DT_NEEDED closure resolves (libonnxruntime-genai.so -> libmat.so).

Built with -DORT_GENAI_VERSION=0.15.0, since the currently pinned 0.15.1 has no GitHub release yet. That gap is being addressed separately by the GenAI team; this PR is about the caching behavior, which is wrong regardless of which versions are published.

@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
foundry-local Ready Ready Preview Aug 7, 2026 8:52pm

Request Review

sheetalarkadam and others added 2 commits August 7, 2026 13:46
GenAI 0.15.0 split most of libonnxruntime-genai.so's implementation into a
separate libmat.so (31 MB arm64 / 30 MB x86_64), and the former now lists it
as a DT_NEEDED:

    libonnxruntime-genai.so -> NEEDED libmat.so

The POST_BUILD copy only ever staged libonnxruntime-genai.so, so an Android
build produced a bin/ that cannot be loaded: libfoundry_local.so resolves
libonnxruntime-genai.so, which then fails on the missing libmat.so.

This is Android-only. GenAI ships Android as a GitHub Releases AAR, and only
that artifact is split; the Microsoft.ML.OnnxRuntimeGenAI.Foundry NuGet
package used by Windows/Linux/macOS is still monolithic and contains no
libmat at all. The copy is therefore guarded on file existence rather than on
ANDROID, which also leaves 0.14.x builds (single monolithic library, no such
dependency) working unchanged.

The Android CI staging step is updated to match, so the published artifact is
a self-contained set.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 24ef037b-246f-4fea-a1a3-4fae5223cb0c
Two independent caching defects in the Android AAR path, both of which
produce a build that silently disagrees with the pinned version.

Stale reuse: the archive was stored as onnxruntime-genai-android.aar and
extracted to a single genai-android-aar/ directory, neither of which
included the version. Both steps are guarded by EXISTS, so bumping
ORT_GENAI_VERSION reused whichever AAR had been downloaded first while
logging the new version. A warm build directory therefore produced binaries
from the old release. CI never saw this because every run starts from a
fresh binary directory; developers hit it on every bump. Scoping both the
archive and the extracted tree by version makes a bump fetch its own copy.

Poisoned entry: file(DOWNLOAD) writes its destination even when the transfer
fails. An unpublished release returns HTTP 404, which yields status 22 and a
0-byte file. The FATAL_ERROR left that file in place, so the next configure
saw EXISTS, skipped the download, and failed inside ARCHIVE_EXTRACT instead
- permanently, until it was deleted by hand. Downloading to a temporary path
and renaming only on success means a failed fetch leaves no cache entry.

The error message now names the version and URL and points at the likely
cause, since a missing Android AAR on an otherwise healthy release is the
common failure.

Verified: 404 leaves no cache entry and retries cleanly; 0.15.0 downloads,
hits cache on re-run, and does not collide with 0.14.0; a clean Android
arm64 build against the GitHub AAR links with a complete DT_NEEDED closure.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 24ef037b-246f-4fea-a1a3-4fae5223cb0c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant