deleting a subscription only partially succeeds, then puts killbill into a strange state, get NullPointerException back from api.

19 views
Skip to first unread message

tenchiro

unread,
Apr 14, 2026, 3:22:21 PM (10 days ago) Apr 14
to Kill Bill users mailing-list
During testing I subscribed to an addon for an active base plan subscription. This was added properly to the bundle. Since I was testing, I then tried cancelling the addon I just subscribed to. I am not exactly sure why, but this only partially succeeded. The billing was stopped, but the entitlement left the door opened. I thought maybe it was some cycle issue so I set it aside. But that was two months ago, and the subscription is still lingering.

When I check subscription state, there is no cancelled date, and state = ACTIVE. But Kaui seems to know it is not active, as it removes all options to cancel or otherwise get rid of it. When I probe, it says no cancelled date, and active. See below.

Trying to cancel it again fails, because it is cancelled. That would be expected, except the state=ACTIVE.

I am curious what is going on. The original failure might have just been the test conditions. I think this was part of a subscribe, then while in trial, add paid options, then cancel options during the trial, and try to resubscribe with policy set to end of term. I could see getting hung up for a cycle, but we are well beyond this now. But more concerning is the inability to fix it. There seems no way to get rid of it now. There must be some kind of "just do it!" option that clears it out. But I haven't been able to find it.

How does Kaui know it is cancelled if state='ACTIVE'?? The only way I can think to hack this is to actually reconstruct the state myself from the events (the cancel event is in there). But that cannot be right? If I understand correctly, that would be the whole point of the state?

And if state cannot be trusted, then what should I be using for entitlement authorization? As it would be possible to signup, quickly cancel, and continue enjoying the feature.

The state as returned showing state and cancelled date:

$ ./test_kbcmd.sh "http://sekai.local:8030/1.0/kb/subscriptions/8c2f1974-b71a-44f0-a230-a444e5510bb0?entitlementPolicy=IMMEDIATE&billingPolicy=IMMEDIATE&callCompletion=true"
http://sekai.local:8030/1.0/kb/subscriptions/8c2f1974-b71a-44f0-a230-a444e5510bb0?entitlementPolicy=IMMEDIATE&billingPolicy=IMMEDIATE&callCompletion=true
{"accountId":"2556aa6f-5f32-4aab-a14c-943fb219828f","bundleId":"b192fed0-3c99-4085-9d4d-b7b07d69a7a5","bundleExternalKey":"b192fed0-3c99-4085-9d4d-b7b07d69a7a5","subscriptionId":"8c2f1974-b71a-44f0-a230-a444e5510bb0","externalKey":"8c2f1974-b71a-44f0-a230-a444e5510bb0","startDate":"2026-02-26T07:45:28.000Z","productName":"app-drone-flightplan","productCategory":"ADD_ON","billingPeriod":"MONTHLY","phaseType":"EVERGREEN","priceList":"DEFAULT","planName":"addon-drone-flightplan","state":"ACTIVE","sourceType":"NATIVE","cancelledDate":null,"chargedThroughDate":"2026-03-09","billingStartDate":"2026-02-26T07:45:28.000Z","billingEndDate":"2026-03-09T01:55:12.000Z","billCycleDayLocal":9,"quantity":1,"events":[{"eventId":"c721f941-7979-4462-8211-22df41ad5d01","billingPeriod":"MONTHLY","effectiveDate":"2026-02-26T07:45:28.000Z","catalogEffectiveDate":"2026-02-26T00:17:00.000Z","plan":"addon-drone-flightplan","product":"app-drone-flightplan","priceList":"DEFAULT","eventType":"START_ENTITLEMENT","isBlockedBilling":false,"isBlockedEntitlement":false,"serviceName":"entitlement-service","serviceStateName":"START_ENTITLEMENT","phase":"addon-drone-flightplan-evergreen","auditLogs":[]},{"eventId":"c721f941-7979-4462-8211-22df41ad5d01","billingPeriod":"MONTHLY","effectiveDate":"2026-02-26T07:45:28.000Z","catalogEffectiveDate":"2026-02-26T00:17:00.000Z","plan":"addon-drone-flightplan","product":"app-drone-flightplan","priceList":"DEFAULT","eventType":"START_BILLING","isBlockedBilling":false,"isBlockedEntitlement":false,"serviceName":"billing-service","serviceStateName":"START_BILLING","phase":"addon-drone-flightplan-evergreen","auditLogs":[]},{"eventId":"c3d98e3c-99c5-4788-899e-26a4f01348fc","billingPeriod":"MONTHLY","effectiveDate":"2026-03-09T01:55:12.000Z","catalogEffectiveDate":"2026-02-26T00:17:00.000Z","plan":"addon-drone-flightplan","product":"app-drone-flightplan","priceList":"DEFAULT","eventType":"STOP_BILLING","isBlockedBilling":false,"isBlockedEntitlement":false,"serviceName":"billing-service","serviceStateName":"STOP_BILLING","phase":"addon-drone-flightplan-evergreen","auditLogs":[]}],"priceOverrides":null,"prices":[{"planName":"addon-drone-flightplan","phaseName":"addon-drone-flightplan-evergreen","phaseType":"EVERGREEN","fixedPrice":null,"recurringPrice":750,"usagePrices":[]}],"auditLogs":[]}Done.

But then, trying to cancel the entitlement side:

$ ./test_kbcmd.sh -X DELETE   "http://sekai.local:8030/1.0/kb/subscriptions/8c2f1974-b71a-44f0-a230-a444e5510bb0?entitlementPolicy=IMMEDIATE&callCompletion=true"   -H "X-Killbill-CreatedBy: admin"   -H "X-Killbill-Reason: force-fix-stale-cancel"

Returns null pointer exception (oh oh!):

{"className":"java.lang.NullPointerException","code":null,"message":null,"causeClassName":null,"causeMessage":null,"stackTrace":[]}Done.

Both billing and entitlement also fails but w/o the null pointer exception.

Checking the subscription again:

...
"state":"ACTIVE"
...

I might be doing something wrong, but I am pretty sure the NullPointerException response is a problem inside the endpoint.

Thank you, any advice appreciated.

Kind regards

karan bansal

unread,
Apr 17, 2026, 2:45:50 AM (7 days ago) Apr 17
to Kill Bill users mailing-list
Hi,

Could you please confirm the version of KB that you are using.

Regards
Karan

tenchiro

unread,
Apr 17, 2026, 6:14:20 AM (7 days ago) Apr 17
to Kill Bill users mailing-list
Wow. That was fast. Let me check it out. Version info below.

[{"nodeName":"46a29f86-94d1-4c59-b499-8a13d711cca8","bootTime":"2026-04-14T09:44:17.000Z","lastUpdatedDate":"2026-04-14T09:44:17.000Z","kbVersion":"0.24.13","apiVersion":"0.54.0","pluginApiVersion":"0.27.3","commonVersion":"0.26.5","platformVersion":"0.41.15","pluginsInfo":[{"bundleSymbolicName":"org.kill-bill.billing.plugin.java.analytics-plugin","pluginKey":"analytics","pluginName":"analytics-plugin","version":"8.1.3","state":"RUNNING","isSelectedForStart":true,"services":[]},{"bundleSymbolicName":"org.kill-bill.billing.killbill-platform-osgi-bundles-kpm","pluginKey":null,"pluginName":"org.kill-bill.billing.killbill-platform-osgi-bundles-kpm","version":null,"state":"RUNNING","isSelectedForStart":true,"services":[]},{"bundleSymbolicName":"org.kill-bill.billing.killbill-platform-osgi-bundles-logger","pluginKey":null,"pluginName":"org.kill-bill.billing.killbill-platform-osgi-bundles-logger","version":null,"state":"RUNNING","isSelectedForStart":true,"services":[]},{"bundleSymbolicName":"org.kill-bill.billing.killbill-platform-osgi-bundles-metrics","pluginKey":null,"pluginName":"org.kill-bill.billing.killbill-platform-osgi-bundles-metrics","version":null,"state":"RUNNING","isSelectedForStart":true,"services":[]},{"bundleSymbolicName":"org.kill-bill.billing.plugin.java.stripe-plugin","pluginKey":"stripe","pluginName":"stripe-plugin","version":"8.0.3","state":"RUNNING","isSelectedForStart":true,"services":[{"serviceTypeName":"javax.servlet.Servlet","registrationName":"killbill-stripe"},{"serviceTypeName":"org.killbill.billing.osgi.api.Healthcheck","registrationName":"killbill-stripe"},{"serviceTypeName":"org.killbill.billing.payment.plugin.api.PaymentPluginApi","registrationName":"killbill-stripe"}]}]}]

karan bansal

unread,
Apr 18, 2026, 11:33:48 AM (6 days ago) Apr 18
to Kill Bill users mailing-list
Hi,

Could you please try with 0.24.16. The status of the add on subscription correctly updated to Cancelled as well as all the events also show up. 

Regards
Karan

tenchiro

unread,
Apr 21, 2026, 11:31:15 AM (3 days ago) Apr 21
to Kill Bill users mailing-list
Thank you. I'll be setting up a new system in a couple of weeks. I can try the new version then and see if it clears it.

I implemented a workaround using the charged through date. It's just a hack that just double checks and transcribes the results passed up from my subsystem to the app. But if anyone has the same problem, they might find it helpful. Kaui must be doing something like this, because it doesn't seem to get fooled.


# - Check to see if there is a cancelled date already (i.e. killbill worked properly)
cancelled_date = raw.get('cancelledDate')
billing_end_date = raw.get('billingEndDate')
state = raw.get('state')
if not cancelled_date and billing_end_date:
# Key off of the billing_end_date. If this is not set either then we are blind.
# CHECK: not flagged cancelled yet, but billing has ended / is ending
end_date = parser.isoparse(billing_end_date)
now_date = datetime.now(timezone.utc)
# Check if ended
if end_date < now_date:
# ENDED ALREADY
#
# Hack the cancel values. These will NOT align with direct
# values from killbill.
cancelled_date = billing_end_date
state = 'CANCELLED'


Reply all
Reply to author
Forward
0 new messages