Significant-Gravitas/AutoGPT · warning · HTTPException

ENTERPRISE subscription changes must be managed by an admini

Error message

ENTERPRISE subscription changes must be managed by an administrator

What it means

HTTP 403 from POST /credits/subscription when the caller's current subscription_tier is ENTERPRISE. Enterprise contracts are provisioned and changed by administrators (custom Stripe pricing, invoices, contracts), so self-service tier changes are blocked for those users regardless of the requested target tier.

Source

Thrown at autogpt_platform/backend/backend/api/features/v1.py:1086

async def update_subscription_tier(
    request: SubscriptionTierRequest,
    user_id: Annotated[str, Security(get_user_id)],
    x_datafast_visitor_id: Annotated[
        str | None, Header(include_in_schema=False)
    ] = None,
    x_datafast_session_id: Annotated[
        str | None, Header(include_in_schema=False)
    ] = None,
) -> SubscriptionStatusResponse:
    # Pydantic validates tier is one of BASIC/PRO/MAX/BUSINESS via Literal type.
    tier = SubscriptionTier(request.tier)

    # ENTERPRISE tier is admin-managed — block self-service changes from ENTERPRISE users.
    user = await get_user_by_id(user_id)
    if (
        user.subscription_tier or SubscriptionTier.NO_TIER
    ) == SubscriptionTier.ENTERPRISE:
        raise HTTPException(
            status_code=403,
            detail="ENTERPRISE subscription changes must be managed by an administrator",
        )

    # Same-tier + same-cycle request = "stay on my current tier" = cancel any
    # pending scheduled change (paid→paid downgrade or paid→BASIC cancel). This
    # replaces the old /credits/subscription/cancel-pending route. Safe when no
    # pending change exists: release_pending_subscription_schedule returns
    # False and we simply return the current status.
    #
    # Same-tier-DIFFERENT-cycle (monthly Pro → yearly Pro, or vice versa) must
    # fall through to modify_stripe_subscription_for_tier so Stripe swaps the
    # price ID for the cycle the user actually requested.
    #
    # Gate the short-circuit on an actual active/trialing Stripe subscription:
    # admin-granted tiers (DB tier set, no Stripe sub) must fall through to the
    # Checkout flow so "start paying for my current tier" is not a no-op.
    current_tier = user.subscription_tier or SubscriptionTier.NO_TIER

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Hide the self-service subscription controls in the frontend when the current tier is ENTERPRISE
  2. Contact the platform administrator / support to change an ENTERPRISE subscription
  3. For testing: have an admin reset the user's tier before retrying self-service flows
Defensive patterns

Strategy: type-guard

Type guard

function canSelfServeSubscription(tier: SubscriptionTier): boolean {
  return tier !== "ENTERPRISE";
}

Try / catch

try { await api.setSubscription(req); }
catch (e) { if (e.status === 403 && /ENTERPRISE/.test(e.detail)) showContactAdmin(); else throw e; }

Prevention

When it happens

Trigger: Any POST /credits/subscription (upgrade, downgrade, or cancel) issued by a user whose user.subscription_tier == ENTERPRISE.

Common situations: Enterprise users clicking through the pricing page after their org was migrated to ENTERPRISE; stale frontend still showing tier-change buttons for enterprise accounts; test users upgraded to ENTERPRISE by an admin script while old UI sessions remain open.

Related errors


AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14). Data as JSON: /api/errors/3f33f8954ad4c6be. Report an issue: GitHub.