alibaba/ARouter · error · NoRouteFoundException

No postcard!

Error message

No postcard!

What it means

LogisticsCenter.completion() fills in a Postcard's route metadata; it throws NoRouteFoundException 'No postcard!' when handed a null Postcard. This is a fail-fast guard: navigation cannot proceed without a Postcard built via ARouter.build().

Source

Thrown at arouter-api/src/main/java/com/alibaba/android/arouter/core/LogisticsCenter.java:274

            addRouteGroupDynamic(postcard.getGroup(), null);
        } catch (Exception e) {
            throw new HandlerException(
                    TAG + "Fatal exception when loading group meta. [" + describeFailure(e) + "]",
                    e
            );
        }

        return Warehouse.routes.containsKey(postcard.getPath());
    }

    /**
     * Completion the postcard by route metas
     *
     * @param postcard Incomplete postcard, should complete by this method.
     */
    public synchronized static void completion(Postcard postcard) {
        if (null == postcard) {
            throw new NoRouteFoundException(TAG + "No postcard!");
        }

        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {
            // Maybe its does't exist, or didn't load.
            if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
                throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
            } else {
                // Load route and cache it into memory, then delete from metas.
                try {
                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                    }

                    addRouteGroupDynamic(postcard.getGroup(), null);

                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));

View on GitHub (pinned to 84f451d244)

Solutions

  1. Ensure the Postcard comes from ARouter.getInstance().build(path) and is non-null before navigating
  2. Null-check inputs in wrapper methods that derive the path from URIs or user data
  3. Log and return early instead of forwarding a null Postcard to the navigation pipeline

Example fix

// before
Postcard postcard = uri != null ? build(uri) : null;
LogisticsCenter.completion(postcard); // NPE-style throw
// after
Postcard postcard = uri != null ? build(uri) : null;
if (postcard != null) {
    LogisticsCenter.completion(postcard);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (postcard == null) {
    Log.w(TAG, "no postcard built, skipping navigation");
    return;
}

Type guard

boolean isValid(Postcard p) { return p != null && p.getPath() != null && !p.getPath().isEmpty(); }

Try / catch

try {
    LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException e) {
    Log.w(TAG, "completion skipped: " + e.getMessage());
}

Prevention

When it happens

Trigger: Passing null to completion(), e.g. a navigation helper that builds a Postcard conditionally and forwards null when a path/group is empty.

Common situations: Custom navigation wrappers where build() result is assigned conditionally; refactored code where an earlier null check on the URI/path was removed.

Related errors


AI-assisted analysis of alibaba/ARouter@84f451d244 (2026-09-06). Data as JSON: /api/errors/adce9c618c284549. Report an issue: GitHub.