Support

Akeeba Backup for Joomla!

#42988 Backblaze B2: b2_authorize_account v1-v3 retired, v4 patch attached

Posted in ‘Akeeba Backup for Joomla!’
This is a public ticket

Everybody will be able to see its contents. Do not include usernames, passwords or any other sensitive information.

Environment Information

Joomla! version
6.1.1
PHP version
8.1.34
Akeeba Backup version
10.3.3

Latest post by nicholas on Thursday, 18 June 2026 15:09 CDT

genr8r

Hi Nicholas,

Scheduled Akeeba Backup B2 uploads on our Joomla 6 site started failing at post-processing on 2026-05-29. Root cause is on Backblaze's side: they retired API versions 1, 2, and 3 of b2_authorize_account specifically. Only v4 still responds. The Backblaze post-processor in Akeeba Backup 10.3.3 calls v1, so every scheduled run with B2 as the destination now stops at the first authorize call. Small patch attached.

The issue

vendor/akeeba/engine/engine/Postproc/Connector/Backblaze.php (line 49) hardcodes /b2api/v1/ as the authorize entry point, and authorizeAccount() at line 805 calls b2_authorize_account against it. As of 2026-05-29:

Backblaze B2 API Error bad_request:
This request is not currently supported on API version number 1

I verified directly against the B2 endpoint with the same key:

v1 bad_request: This request is not currently supported on API version number 1
v2 bad_request: ... version number 2
v3 bad_request: ... version number 3
v4 200 OK, new nested response shape

The cutoff was narrow: our last successful Akeeba B2 upload finished at 2026-05-28 23:54 UTC; the next scheduled run at 2026-05-29 03:35 UTC hit the rejection. The other Backblaze endpoints used by the connector (b2_get_upload_url, b2_upload_file, b2_list_file_versions, etc.) still respond on v1 today, so the patch only touches the authorize call site and the two response objects.

Every Akeeba Backup install pointed at Backblaze B2 is going to hit this as their scheduled runs fire.

Proposed fix
  1. Backblaze.php: change apiURL from /b2api/v1/ to /b2api/v4/. The constant is only used by authorizeAccount(); other endpoints run off the per-account apiUrl returned by authorize, unchanged.
  2. AccountInformation.php: in the constructor, detect the v4 nested shape and lift apiInfo.storageApi.* back to the top level before the existing property-assignment loop. v1 still works because the lift only triggers when apiInfo.storageApi is present.
  3. Allowed.php: add a $buckets property for the v4 allowed.buckets[] array (which replaces bucketId/bucketName for multi-bucket-scoped keys). Seed bucketId/bucketName from the first entry so single-bucket callers keep working. Update isBucketAllowed() to check the full bucket list when present.

Multi-bucket support in Allowed was necessary in our case because the same runtime key has access to four buckets (GFS rotation). Without it, the seeded bucketName only matches the first bucket and rejects the others.

Full diff attached. 110 lines added, 0 removed. The headline change:

--- Backblaze.php
+++ Backblaze.php
@@ -45,8 +45,19 @@
-	/** The API entry point URL, only used to retrieve the authorization token */
-	public const apiURL = "https://api.backblazeb2.com/b2api/v1/";
+	/**
+	 * The API entry point URL, only used to retrieve the authorization token.
+	 * Backblaze retired v1, v2 and v3 of b2_authorize_account around 2026-05-28/29.
+	 * Only v4 is accepted now. AccountInformation and Allowed unpack both shapes.
+	 */
+	public const apiURL = "https://api.backblazeb2.com/b2api/v4/";
Alternative shapes
  • I left the other nine /b2api/v1/ path constants in Backblaze.php alone because they still respond on v1 today. If you'd rather migrate the whole connector in one pass, it is mechanical; I can attach a broader patch.
  • The v4 backward-compat path in AccountInformation and Allowed could be removed once it is clear Backblaze is not rolling v1 back. I kept it for safety during the rollover window.
Tested

Patched on our production install, verified end-to-end via Akeeba's composer autoloader against a fresh v4 authorize response and confirmed multi-bucket isBucketAllowed() behaves correctly. Tonight's scheduled GFS daily will exercise the full B2 upload path live.

Thanks for taking a look. Happy to reshape, broaden to migrate the rest of the v1 paths in one go, or drop the backward-compat handling if you'd prefer a cleaner v4-only path.

Brian Mitchell
Intergen

genr8r

Quick follow-up: this also affects Akeeba Backup for WordPress (verified on 9.1.2).

The three connector files at vendor/akeeba/engine/engine/Postproc/Connector/{Backblaze.php, Backblaze/AccountInformation.php, Backblaze/Allowed.php} are byte-different from the AB-Joomla 10.3.3 copies (different copyright lines, minor formatting) but the actual hunks land cleanly with zero fuzz. The attached patch applies to AB-WP with only the working-directory adjustment (the files live under app/vendor/... instead of administrator/components/com_akeebabackup/vendor/...).

I applied it on a production WP install today after a 22-day silent-failure window: every nightly run reported status=complete but with remote_filename=NULL on the _ak_stats rows (same abort_on_fail shape we discussed in the original report). Three patched files php -l clean. Tonight's scheduled run is the live test on that side too.

Same shared engine, same bug, same fix. Both products affected.

moira

Thank you for the detailed report and the ready-made patch — this saved a significant amount of investigation time.

You are correct on all counts. Backblaze silently retired v1–v3 of b2_authorize_account on 2026-05-28/29, and our connector was still targeting v1. We have applied the fix to the shared Akeeba Engine library that underlies Akeeba Backup for Joomla!, Akeeba Backup for WordPress, and Akeeba Solo. The three changed files match your analysis:

  • Backblaze.php: apiURL constant updated to v4.
  • AccountInformation.php: v4 nested apiInfo.storageApi shape is lifted to the top level before property assignment, so both shapes are handled transparently.
  • Allowed.php: $buckets property added; bucketId/bucketName are seeded from the first entry for backward compatibility; isBucketAllowed() checks the full bucket list for multi-bucket keys.

A new version containing this fix will be released next week.

Moira Fari

Support Specialist

🇬🇧English: native 🕐 My time zone is Asia / Nicosia
Kindly note that my replies are fully vetted by our developers.

genr8r

Thanks for the fast turnaround on this, Moira!

One follow-up: 10.3.4 auto-installed on one of our production sites on 2026-06-05, and its CHANGELOG only lists two Quota-page fixes (no B2 entry). The Backblaze connector reverted to the v1 endpoint and our scheduled B2 uploads went silent again until we re-applied the patch this morning. Easy to confirm with grep -c 'b2api/v4' Backblaze.php on a stock 10.3.4 install (currently returns 0).

Do you happen to know which release will carry the fix? We've got a re-apply checklist on the affected sites until then, and knowing the target release would help us scope how long to keep it active. Happy to test against a pre-release build if that's useful on your end.

Thanks again!

nicholas
Akeeba Staff
Manager

Version 10.3.4 had undergone final validation on Friday, 29th May. It was only released on Monday, 1st June just because I never do non-emergency releases on a Friday. The path for B2 will be included in the release after that.

Sorry for the miscommunication.

Nicholas K. Dionysopoulos

Lead Developer and Director

🇬🇷Greek: native 🇬🇧English: excellent 🇫🇷French: basic • 🕐 My time zone is Europe / Athens
Please keep in mind my timezone and cultural differences when reading my replies. Thank you!

genr8r

Never a worry Nicholas,

I figured something along those lines. If it had somehow slipped through the cracks, I just wanted to be sure it didn't go another release cycle before I pinged it.

Thank you and Moira for the great support as always!

Brian

nicholas
Akeeba Staff
Manager

You're welcome, and thank you for the understanding!

Nicholas K. Dionysopoulos

Lead Developer and Director

🇬🇷Greek: native 🇬🇧English: excellent 🇫🇷French: basic • 🕐 My time zone is Europe / Athens
Please keep in mind my timezone and cultural differences when reading my replies. Thank you!

genr8r

Hi Nicholas,

Following up on this ticket. The v4 b2_authorize_account migration shipped (I am now on Akeeba Backup Pro 10.3.5), thank you. One part of the v4 response handling still does not match the live Backblaze API, and it breaks B2 post-processing for any bucket-restricted application key, which is the least-privilege setup you would normally recommend.

The remaining issue

In vendor/akeeba/engine/engine/Postproc/Connector/Backblaze/Allowed.php, the v4 seeding reads the bucket entry keys as bucketId / bucketName:

$first            = (array) reset($this->buckets);
$this->bucketId   = $first['bucketId'] ?? '';
$this->bucketName = $first['bucketName'] ?? '';

But the live b2_authorize_account v4 response returns allowed.buckets[] entries keyed id / name (captured today, key restricted to one bucket):

"allowed": {
  "buckets": [ { "id": "xxxxxxxxxxxxxxxxxxxxxxxx", "name": "my-bucket" } ],
  "capabilities": [ "writeFiles", "listFiles" ],
  "namePrefix": "myprefix/"
}

So $first['bucketId'] and $first['bucketName'] are both unset, and allowed->bucketName ends up empty. Backblaze::getBucketId() then never matches its fast path:

if ($this->getAccountInformation()->allowed->bucketName == $name)
{
    return $this->getAccountInformation()->allowed->bucketId;
}
// ...falls through to:
$this->buckets = $this->listBuckets();

and falls through to listBuckets(), which calls b2_list_buckets with no bucket filter. Backblaze rejects that for any key restricted to a bucket (and the call also needs the listBuckets capability, which a least-privilege writeFiles,listFiles key does not carry). The result is that the bucket can never be resolved and the upload fails. isBucketAllowed() has the same bucketName-keyed lookup a few lines down.

Fix

Read id / name with the old keys kept as a fallback, in the seeding block and in isBucketAllowed(). Three lines, additive and backward-compatible:

@@ seeding block @@
-		$this->bucketId   = $first['bucketId'] ?? '';
-		$this->bucketName = $first['bucketName'] ?? '';
+		$this->bucketId   = $first['bucketId'] ?? $first['id'] ?? '';
+		$this->bucketName = $first['bucketName'] ?? $first['name'] ?? '';

@@ isBucketAllowed() @@
-			if (($entry['bucketName'] ?? '') === $bucket)
+			if (($entry['bucketName'] ?? $entry['name'] ?? '') === $bucket)

Full diff attached as b2-buckets-id-name.patch (apply with git apply from the component root). The same vendored engine ships in Akeeba Backup for WordPress, so the WP copy needs the identical change.

Environment where reproduced
  • Akeeba Backup: Pro 10.3.5 (stock, unmodified before this patch)
  • Joomla: 6.1.1, PHP 8.3 (cli)
  • Backblaze B2 runtime key: restricted to a single bucket, name-prefix scoped, capabilities writeFiles,listFiles (no listBuckets)
  • Native Backblaze B2 post-processing engine, scheduled CLI backup

Before the patch, getBucketId() could not resolve the bucket for the restricted key. After the patch, scheduled daily and weekly backups package locally and upload to B2 successfully (verified by listing the objects in the bucket with the same key).

This is against 10.3.5; if a later build already adjusted this, please disregard. Happy to adjust the shape if you would prefer to normalize id/name to bucketId/bucketName once at the AccountInformation level instead of at each read site.

Brian

genr8r
forgot the attachment

nicholas
Akeeba Staff
Manager

Thank you for the feedback! This is a use case I had not implemented in the integration tests. I do have a single-bucket key, but it also had the listBuckets permissions which caused this code path to be skipped. I understand why I was doing that; coming from S3, not having listBuckets is… inconceivable. Sorry, I couldn't resist the Princess Bride reference.

Yes, now that I have set up a key exactly like that I can see the problem and the necessary fix. Your suggestion is actually pretty good. The null coalesce is probably the most straightforward way to do that and it does pass regression testing with flying colors.

Nicholas K. Dionysopoulos

Lead Developer and Director

🇬🇷Greek: native 🇬🇧English: excellent 🇫🇷French: basic • 🕐 My time zone is Europe / Athens
Please keep in mind my timezone and cultural differences when reading my replies. Thank you!

Support Information

Working hours: We are open Monday to Friday, 9am to 7pm Cyprus timezone (EET / EEST). Support is provided by the same developers writing the software, all of which live in Europe. You can still file tickets outside of our working hours, but we cannot respond to them until we're back at the office.

Support policy: We would like to kindly inform you that when using our support you have already agreed to the Support Policy which is part of our Terms of Service. Thank you for your understanding and for helping us help you!