Knife upload --diff --freeze throws errors for unchanged cookbooks

We're mid-implemention on a whole new chef infra server project and are running into an issue that we just can't figure out. If anyone has any thoughts I'd be grateful!

During our chef-repo pipeline upload we're attempting to use --diff and --freeze to only upload those cookbooks that are changed, while also requiring version bumps. However the use of these together seems to result in non-zero failures, even if the correct action is taken. Here's the situation:

$ knife -v
Chef Infra Client: 16.2.73

$ chef-server-ctl version
13.2.0

chef-repo/cookbooks/
/cookbook1
/cookbook2
/cookbook3
/example

The initial upload of any given cookbook works. However running with --diff and --freeze when there are no changes results in a non-zero exit:

$ knife upload --chef-repo-path . . --diff --freeze --profile doc --verbose
INFO: Using configuration from /Users/ben.fielden/.chef/knife.rb
INFO: Validating ruby files
INFO: Validating templates
INFO: Syntax OK
INFO: Saving cookbook1
INFO: Validating ruby files
INFO: Validating templates
INFO: Syntax OK
INFO: Saving cookbook2
INFO: Validating ruby files
INFO: Validating templates
INFO: Syntax OK
INFO: Saving cookbook3
INFO: Uploading files
INFO: Validating ruby files
INFO: Validating templates
INFO: Syntax OK
INFO: Saving example
INFO: Uploading files
INFO: Uploading files
ERROR: cookbooks failed to write: Cookbook cookbook1 is frozen
INFO: Uploading files
ERROR: cookbooks failed to write: Cookbook cookbook2 is frozen
ERROR: cookbooks failed to write: Cookbook cookbook3 is frozen
ERROR: cookbooks failed to write: Cookbook example is frozen

If I then change a minor attribute and bump the 'example' cookbook metadata.rb it successfully uploads, but still non-zero exits for the other 3 freeze errors:

$ knife upload --chef-repo-path . . --diff --freeze --profile doc --verbose
INFO: Using configuration from /Users/user/.chef/knife.rb
INFO: Validating ruby files
INFO: Validating templates
INFO: Syntax OK
INFO: Saving cookbook1
INFO: Validating ruby files
INFO: Validating templates
INFO: Syntax OK
INFO: Saving cookbook2
INFO: Uploading files
INFO: Validating ruby files
INFO: Validating templates
INFO: Syntax OK
INFO: Saving cookbook3
INFO: Validating ruby files
INFO: Validating templates
INFO: Syntax OK
INFO: Saving example
INFO: Uploading files
INFO: Uploading files
ERROR: cookbooks failed to write: Cookbook cookbook1 is frozen
INFO: Uploading files
INFO: Uploading /var/folders/gz/hash234/T/tmp_working_dir_path20200721-35420-fdmipl/example/metadata.json (checksum hex = sum1) to URL_HERE
INFO: Uploading /var/folders/gz/hash234/T/tmp_working_dir_path20200721-35420-fdmipl/example/attributes/default.rb (checksum hex = sum2) to URL_HERE
INFO: Uploading /var/folders/gz/hash234/T/tmp_working_dir_path20200721-35420-fdmipl/example/metadata.rb (checksum hex = sum3) to URL_HERE
ERROR: cookbooks failed to write: Cookbook cookbook3 is frozen
ERROR: cookbooks failed to write: Cookbook cookbook2 is frozen
INFO: Upload complete!
Updated cookbooks/example

All of our cookbooks simply maintain metadata.rb without metadata.json. However it looks like knife is generating the json file at upload. I am assuming that the reason we're failing is because the cookbooks stored in our repo don't include metadata.json, but it's present on the server thus triggering the diff.

Do we have to maintain both metadata.rb and metadata.json for each cookbook in order for this to work? This page: https://chef.readthedocs.io/en/latest/metadata_rb.html seems to indicate we shouldn't be editing the json file at all for anything permanent.

This is problematic for us because non-zero exit codes mean that setting up a pipeline and detecting actual errors will require output parsing. We could probably avoid using --diff, but our previous implementation had ~200 cookbooks so doing a full upload everytime isn't ideal.

I feel like maybe I'm missing something, since I can't imagine this is an edge use-case. Any guidance would be helpful