OpenKeel: the sovereign French Cloud solution of the inherent group, with a European reach Discover inherent

Reverse-spec · Clients

python-barbicanclient — Errors

Errors — python-barbicanclient — branch stable/2026.2

Branch stable/2026.2 @ 8162006e8677 — repository https://opendev.org/openstack/python-barbicanclient — analysed on 2026-11-01

A case marked [observed] was reproduced with a real process against the local stub; its ID is in defect-repro. The stub answers what the case programs. So [observed] proves how the client reads an answer, not that a real Barbican sends it; that part is stated separately.

1. What the user sees

ExitMeaningSource
0success. cliff turns a None from take_action into 0cliff:cliff/command.py:196
1any exception in a command, message on stderr, no traceback without --debugcliff:cliff/app.py:462
2usage error from argparse, or unknown commandcliff:cliff/app.py:428

An HTTP error prints twice [observed]: the client logs 4xx Client error: <message> (barbicanclient/client.py:132), and cliff logs the exception (cliff:cliff/app.py:467). Under barbican, each line prints 3 times (§ 4, D16).

An exception that is not an HTTP error is printed as Python renders it: a KeyError prints only 'title' [observed].

2. HTTP translation

The adapter sets raise_exc=False (barbicanclient/client.py:92) and checks the status itself (barbicanclient/client.py:117):

StatusExceptionLoggedSource
401, after one re-authentication by keystoneauthHTTPAuthErrorAuth error: %sbarbicanclient/client.py:120
500 or more, or no statusHTTPServerError5xx Server error: %sbarbicanclient/client.py:125
other 400 or moreHTTPClientError4xx Client error: %sbarbicanclient/client.py:131
below 400none

The message comes from the body (barbicanclient/client.py:138):

  • JSON with title and description: <title>: <description>.
  • JSON with title only: <title>.
  • JSON without title: crash, D1.
  • not JSON: the raw bytes, printed as b'…' [observed: error-html-502], D15.

Barbican's own errors have this shape (barbican:barbican/api/hooks.py:29). Errors raised before the application, such as keystonemiddleware's, do not (keystonemiddleware:keystonemiddleware/auth_token/__init__.py:682).

Discovery errors do not pass through this code: get_version_data uses the session with exceptions on (keystoneauth1:keystoneauth1/discover.py:112), so they surface as keystoneauth exceptions, except the 406 that the client catches (barbicanclient/v1/client.py:132).

Note that secret delete at 1.2 converts a client error holding the text Secret cannot be deleted as it has consumers. to SecretHasConsumers (barbicanclient/v1/secrets.py:604). The service sends it with status 400 (barbican:barbican/common/exception.py:174).

3. Catalogue

Exception classes (9, from the inventory)

ClassBaseSituationRaised atCaught at
BarbicanExceptionExceptionbase onlycastellan:castellan/key_manager/barbican_key_manager.py:363
PayloadExceptionBarbicanExceptionwrong or empty payload, on the clientbarbicanclient/v1/secrets.py:349, barbicanclient/v1/secrets.py:352, barbicanclient/v1/secrets.py:401, barbicanclient/v1/secrets.py:410, barbicanclient/v1/secrets.py:541
UnsupportedVersionBarbicanExceptionfactory failure, see D3barbicanclient/client.py:220
SecretHasConsumersBarbicanExceptionfixed message; deletion refusedbarbicanclient/v1/secrets.py:607, barbicanclient/v1/secrets.py:614
HTTPErrorExceptionbase, holds status_codecastellan:castellan/key_manager/barbican_key_manager.py:364
HTTPClientErrorHTTPError4xxbarbicanclient/client.py:133, barbicanclient/v1/secrets.py:609barbicanclient/v1/secrets.py:604
HTTPServerErrorHTTPError5xxbarbicanclient/client.py:127
HTTPAuthErrorHTTPError401; constructor broken, D2barbicanclient/client.py:122
ImmutableExceptionException (barbicanclient/base.py:59)setter or second store() after savebarbicanclient/v1/secrets.py:42, barbicanclient/v1/orders.py:32, barbicanclient/v1/containers.py:33

BarbicanException and HTTPError have no common base. A KeyError or TypeError from a malformed response is neither, so castellan's handlers do not catch it [inferred from castellan:castellan/key_manager/barbican_key_manager.py:362].

Client-side messages reachable from a command

These send no business request, though authentication and discovery may already have run.

MessageExceptionSituationRaised atObserved in
Secret incorrectly specified. (also Container, Order, CA, secret)ValueErrorthe last segment of the URI is not a UUIDbarbicanclient/base.py:48get-not-a-uuid, get-payload-url
Must supply at least one secret.ValueErrorsecret container create without --secretbarbicanclient/barbican_cli/v1/containers.py:135base probe
Invalid container type specified.ValueError--type not in generic, rsa, certificatebarbicanclient/barbican_cli/v1/containers.py:100container-unknown-type
One or more of the provided secret_refs could not be retrieved!ValueErrora ref is not parseable; nothing is retrieved despite the textbarbicanclient/v1/containers.py:103container-secret-not-uuid
ERROR: file already exists.ValueErrorsecret get --file on an existing file, after the GETbarbicanclient/barbican_cli/v1/secrets.py:81get-file-exists
Invalid Payload: Cannot Be Empty StringPayloadExceptionempty --filebarbicanclient/v1/secrets.py:352store-empty-file
Invalid Payload TypePayloadExceptionnot str or bytes; Python APIbarbicanclient/v1/secrets.py:349, barbicanclient/v1/secrets.py:541
Unable to parse date string 'soon'ValueError from oslo.utilsbad --expirationbarbicanclient/v1/secrets.py:445, barbicanclient/v1/orders.py:128store-bad-expiration
Server does not support secret consumers. Minimum key-manager microversion required: 1.1NotImplementedErrorconsumer commands at 1.0barbicanclient/v1/secrets.py:685microversion-1.0-consumer-list
Secret cannot be deleted as it has consumers.SecretHasConsumersbelow 1.2, client-sidebarbicanclient/v1/secrets.py:614microversion-1.1-delete-with-consumers
Secret or container URI is not specified.ValueErrorACL with a bare UUID or other pathbarbicanclient/v1/acls.py:418acl-bare-uuid
Secret ACL URI provided. Expecting Secret URI.ValueErrorthe URI contains /aclbarbicanclient/v1/acls.py:374acl-uri-of-acl
ACL data for secret is not provided.ValueErroracl user remove when the GET gave no ACLbarbicanclient/v1/acls.py:309acl-remove-without-acl
Unknown container type "".TypeErrorresponse type absent or unknownbarbicanclient/v1/containers.py:552200 probe
Unknown Order type "None"TypeErrorresponse type unknown, D6barbicanclient/v1/orders.py:361order-get-unknown-type
Invalid KeyOrder meta field: […]TypeErrorunknown key in the meta of a key orderbarbicanclient/v1/orders.py:353order-key-unknown-meta
Invalid client version v1; must be one of: v1UnsupportedVersionD3barbicanclient/client.py:220discovery-no-max-version
Secret does not contain a payload (warning, not an error)loggedpayload fetch raised ValueError, D10barbicanclient/v1/secrets.py:238get-binary-as-text

Messages reachable only from the Python API or the legacy shell

MessageExceptionRaised at
Barbican endpoint url must be provided when not using auth in the Keystone Session.ValueErrorbarbicanclient/client.py:199
Project ID must be provided when not using auth in the Keystone SessionValueErrorbarbicanclient/client.py:202
Invalid version: …ValueErrorbarbicanclient/client.py:65
Invalid microversion …: Microversion requested is not supported by the clientValueError, masked by D3barbicanclient/v1/client.py:85
Invalid microversion …: Microversion requested is not supported by the serverValueError, masked by D3barbicanclient/v1/client.py:106
Couldn't find a version supported by both client and serverValueError, masked by D3barbicanclient/v1/client.py:100
Endpoint does not support selected microversionValueError, masked by D3; unreachable, since the method never returns None [inferred]barbicanclient/v1/client.py:59
Invalid or Missing PayloadPayloadExceptionbarbicanclient/v1/secrets.py:401
Secret is not yet stored.LookupError, also raised for a container, with the wrong nounbarbicanclient/v1/secrets.py:403, barbicanclient/v1/secrets.py:424, barbicanclient/v1/containers.py:222
Order is not yet stored.LookupErrorbarbicanclient/v1/orders.py:220
Secret has no encrypted data to decrypt., Must specify decrypt content-type…ValueError, caught by the payload propertybarbicanclient/v1/secrets.py:314, barbicanclient/v1/secrets.py:316
Must provide a valid Secret object, A secret with this name already exists!ValueError, KeyError, turned into the message below when in a constructorbarbicanclient/v1/containers.py:181, barbicanclient/v1/containers.py:183
One or more of the provided secrets are not valid Secret objects!ValueErrorbarbicanclient/v1/containers.py:110
`add()` is not implemented for Typed ContainersNotImplementedErrorbarbicanclient/v1/containers.py:372, barbicanclient/v1/containers.py:510
No Order type provided, Unknown Order type "…"TypeErrorbarbicanclient/v1/orders.py:365, barbicanclient/v1/orders.py:368
Users value is expected to be provided as list/set.ValueErrorbarbicanclient/v1/acls.py:165
… href is required., Invalid ref_type: …, Expected secret or container URI is not specified.ValueErrorbarbicanclient/v1/acls.py:370, barbicanclient/v1/acls.py:409, barbicanclient/v1/acls.py:512
Secret URI is not specified.ValueError, with a ref_typebarbicanclient/v1/acls.py:380
container_ref is required., order_ref is required., secret_ref is required.ValueError; the last is unreachable, since validation comes firstbarbicanclient/v1/containers.py:699, barbicanclient/v1/orders.py:427, barbicanclient/v1/secrets.py:534, barbicanclient/v1/secrets.py:595
Container … could not be found., Order … could not be found.LookupError, behind except AttributeError, which _api.get does not raise for a 404 [inferred]barbicanclient/v1/containers.py:245, barbicanclient/v1/containers.py:544, barbicanclient/v1/orders.py:338
ERROR: argument --os-auth-url/-A: not allowed with argument --no-auth/-NException, legacybarbicanclient/barbican.py:172
ERROR: please specify --endpoint and --os-project-id (or --os-tenant-id)Exception, legacybarbicanclient/barbican.py:180
ERROR: please specify --os-auth-urlException, legacybarbicanclient/barbican.py:192
ERROR: please specify authentication credentialsException, legacybarbicanclient/barbican.py:223
ERROR: please specify the following --os-project-id or …, ERROR: please specify --os-tenant-id or …Exception, legacybarbicanclient/barbican.py:109

Asynchronous errors

None in the client (05). An order that fails on the service shows as Status ERROR, with Error code and Error message taken from error_status_code and error_reason (barbicanclient/v1/orders.py:57).

Key log messages

LevelMessageSituationSource
ERROR4xx Client error: %s, 5xx Server error: %s, Auth error: %sany HTTP errorbarbicanclient/client.py:121
WARNINGSecret does not contain a payloadD10barbicanclient/v1/secrets.py:238
WARNINGDEPRECATION WARNING: Manually setting the payload_content_type…property setters; no command triggers it [inferred: the commands pass it to the constructor]barbicanclient/v1/secrets.py:295
WARNINGThis Barbican CLI interface has been deprecated…every barbican startbarbicanclient/barbican.py:382
INFOCalculated %s uuid ref: %svisible with -v [inferred]barbicanclient/base.py:55

4. Defects

Only what was reproduced, or read in code on both sides, is listed. Real impact says what is known about a real service; none was run.

#DefectSourceEvidenceReal impact
D1**A JSON error without top-level title crashes the error path.** KeyError is not caught: only ValueError is. The user reads 'title'; the status and exception type are lost. A JSON list gives list indices must be integers or slices, not strbarbicanclient/client.py:141, barbicanclient/client.py:145[observed] error-no-title-404, error-keystonemiddleware-401, error-json-listkeystonemiddleware's 401 body has error.title, not title (keystonemiddleware:keystonemiddleware/auth_token/__init__.py:682), so a rejected token should give this [inferred]. Also any proxy or gateway JSON error
D2**HTTPAuthError calls the wrong base.** super(HTTPError, self) skips HTTPError.__init__: status_code is never set, and str(e) is the tuple ('Unauthorized: d', 401)barbicanclient/exceptions.py:62[observed] error-401-with-title, error-401-attr-pyapievery 401 with a title; code reading e.status_code gets AttributeError. castellan reads status_code on HTTPClientError only (castellan:castellan/key_manager/barbican_key_manager.py:706)
D3The factory hides discovery failures. except (KeyError, ValueError) wraps the whole client constructor, so a version document without max_version, a range below the client's, and a bad microversion= all read Invalid client version v1; must be one of: v1barbicanclient/client.py:215[observed] discovery-no-max-version, discovery-range-below-client, bad-microversion-pyapiany service whose version document differs; the shipped service has max_version (barbican:barbican/api/controllers/versions.py:98)
D4A 406 on negotiation leads to the highest version, not the lowest. (None, None) is returned (barbicanclient/v1/client.py:133), version_between treats no bound as a match (keystoneauth1:keystoneauth1/discover.py:415), and the loop picks 1.2. The following request carries key-manager 1.2barbicanclient/v1/client.py:97[observed] discovery-406-on-1.1not reached with Barbican stable/2026.2, which accepts 1.1 (barbican:barbican/api/middleware/microversion.py:35). The Barbican reverse-spec reads the 406 as "server only supports 1.0": that is the comment's intent, not what happens
D5A new field in a response is fatal. Secrets, consumers and orders are built with **: Secret.__init__() got an unexpected keyword argument 'new_field'. secret list fails entirely. A secret consumer POST fails after the consumer was createdbarbicanclient/v1/secrets.py:679, barbicanclient/v1/secrets.py:717, barbicanclient/v1/secrets.py:767, barbicanclient/v1/orders.py:359[observed] secret-list-unknown-field, consumer-create-unknown-field, consumer-list-unknown-field, order-get-unknown-fieldblocks any additive evolution of those documents. Containers are tolerant: they pick fields (barbicanclient/v1/containers.py:555)
D6**The unknown order type message prints None**: it formats the class lookup, not the type. One such order stops the whole secret order listbarbicanclient/v1/orders.py:361[observed] order-get-unknown-type, order-list-one-unknown-typethe service validates only key and asymmetric (barbican:barbican/common/validators.py:436). Old certificate orders in a database should trigger it [inferred]
D7**-f json fails on RSA and certificate containers.** Their formatter returns created as a datetime, where the generic one uses isoformat(). stdout gets partial JSON, exit 1. On secret container create --type rsa -f json, the container was createdbarbicanclient/v1/containers.py:294, barbicanclient/v1/containers.py:415, cliff:cliff/formatters/json_format.py:73[observed] rsa-container-get-json, certificate-container-get-json, rsa-container-create-jsonindependent of the service, once created is set. Table and YAML work (certificate-container-get-yaml) but show a different date format; list uses the generic formatter (rsa-container-list-json)
D8An unnamed secret is re-fetched on each property. The guard is self._secret_ref and not self._name, so a null name never satisfies it. secret get: 10 GET, 11 with an expiration. secret store: 1 POST and 10 GET. secret list of 2: 21 requests. With a name: 1barbicanclient/v1/secrets.py:468[observed] unnamed-secret-get-requests, unnamed-secret-get-with-expiration, unnamed-secret-store-requests, unnamed-secret-list-requests, named-secret-get-requests, named-secret-list-requestsa name is optional, so the load on a real service follows [inferred]. The same logic applies to CAs until plugin_name is set (barbicanclient/v1/cas.py:173): 8 GET in 200 mode
D9**secret store --name prints empty fields.** The same guard skips the reload: Created, Status and Content types are None. Without --name they are filled. secret order create always shows Status None, and a generic secret container create shows Created None, because it reads created before the reloadbarbicanclient/v1/secrets.py:468, barbicanclient/v1/containers.py:59, barbicanclient/v1/orders.py:206[observed] secret-store-named-output, secret-store-unnamed-output, container-secret-without-equalsindependent of the service; scripts cannot rely on those fields
D10An undecodable payload is reported as no payload. UnicodeDecodeError is a ValueError, caught by the property. secret get -p: Payload None, exit 0. With --file: an empty file is created, then a bytes-like object is required, not 'NoneType', exit 1barbicanclient/v1/secrets.py:237, barbicanclient/v1/secrets.py:325, barbicanclient/barbican_cli/v1/secrets.py:87[observed] get-binary-as-text, get-binary-as-text-to-fileBarbican answers 406 instead for text it cannot decode (barbican:barbican/plugin/util/translations.py:103), so this needs a service with another behaviour
D11**--file with -t or -e fails on binary content** with a raw codec error, because the "backward compatibility" branch decodes as UTF-8. The user who gives the correct type for a binary file gets the crash; omitting it worksbarbicanclient/v1/secrets.py:363[observed] store-binary-file-with-content-typeclient-side, no request
D12**--payload without -t is stored as binary**, and the help says -t is required with --payloadbarbicanclient/barbican_cli/v1/secrets.py:165, barbicanclient/barbican_cli/v1/secrets.py:196, barbicanclient/v1/secrets.py:370[observed] store-text-payload-default-typecontent_types shows octet-stream for a text secret. A read with Accept: text/plain still works, since the service decodes (barbican:barbican/plugin/util/translations.py:100) [inferred end to end]
D13**--secret without exactly one = is dropped silently.** --secret foo and a ref whose query holds = both give "secret_refs": [], exit 0 on the stub. Unexpected names on typed containers vanish likewisebarbicanclient/barbican_cli/v1/containers.py:138, barbicanclient/barbican_cli/v1/containers.py:103[observed] container-secret-without-equals, container-secret-with-two-equals, container-rsa-missing-keyon a real service a generic container with no secrets would be created [inferred]; rsa should be refused (barbican:barbican/common/validators.py:732)
D14**--debug writes secrets to stderr.** The curl-style request holds the payload, the response body is logged for text/plain, and osc-lib's start line holds --payload and --os-password in clearkeystoneauth1:keystoneauth1/session.py:602, keystoneauth1:keystoneauth1/session.py:60, osc-lib:osc_lib/shell.py:434[observed] debug-log-payload-store, debug-log-payload-getany real use of --debug. barbicanclient/v1/secrets.py:389 masks only the client's own line
D15**Non-JSON errors print as b'<html>…'**, and success responses that are not JSON raise Expecting value: line 1 column 1 (char 0), including after a successful POST or PUTbarbicanclient/client.py:146, barbicanclient/client.py:102, barbicanclient/client.py:107, barbicanclient/v1/acls.py:328[observed] error-html-502, post-empty-body, acl-put-empty-bodyHTML errors from a proxy; Barbican itself answers JSON on these routes (barbican:barbican/api/controllers/acls.py:181)
D16Legacy shell output: messages print 3 times (logging.basicConfig, plus configure_logging twice). Without OS_IDENTITY_API_VERSION it writes WARNING: The identity version <> is not in supported versions, with no newline, although 3 is the documented default. The deprecation text names the "O release"barbicanclient/barbican.py:381, barbicanclient/barbican.py:349, barbicanclient/barbican.py:142, barbicanclient/barbican.py:382[observed] legacy-password, legacy-password-error-log, legacy-no-credentialsclient-side
D17**acl * refuses a bare UUID**, although URI works that way everywhere else, and the docstrings accept one; the CLI has no way to give ref_typebarbicanclient/v1/acls.py:412[observed] acl-bare-uuidclient-side
D18**A host name holding acl is refused**: the test is a substring on the whole URL, so http://acl.localhost/v1/secrets/<uuid> gives Secret ACL URI providedbarbicanclient/v1/acls.py:373[observed] acl-host-contains-acla deployment whose host name or path starts with acl
D19**acl user add sends duplicates**: ["u1", "u1"]barbicanclient/barbican_cli/v1/acls.py:199[observed] acl-add-duplicatewhat the service stores was not followed
D20**secret order create asymmetric defaults to aes, 256**, inherited from the shared option defaults, and --mode is ignoredbarbicanclient/barbican_cli/v1/orders.py:31, barbicanclient/v1/orders.py:272[observed] order-asymmetric-defaults, order-asymmetric-mode-ignoredthe service accepts it at validation (barbican:barbican/common/validators.py:477); the generation stage should refuse or fail it [inferred]
D21Missing keys give bare errors. {} on GET /orders/{id} prints 'type'; a key order without meta prints 'meta'; a container without secret_refs prints 'NoneType' object is not iterable; an empty version list via --endpoint prints list index out of rangebarbicanclient/v1/orders.py:344, barbicanclient/v1/orders.py:349, barbicanclient/v1/containers.py:614, barbicanclient/v1/client.py:135[observed] 200 probe, order-no-meta, container-no-secret-refs, legacy-discovery-empty-listonly with a non-conforming service
D22**A POST answered 200 {} looks like success**: Secret href None, exit 0barbicanclient/v1/secrets.py:393[observed] post-200-no-refonly with a non-conforming service
D23**ca get, ca list have no route in the service of this branch**barbicanclient/v1/cas.py:236, barbican:barbican/api/controllers/versions.py:141read on both sides; the stub answered as programmed404 expected [inferred]
D24**add_secret_metadata posts to a route the service refuses**: POST /secrets/{id}/metadata/{key} gets pecan.abort(405), and the service's POST is at /metadatabarbicanclient/v1/secrets.py:808, barbican:barbican/api/controllers/secrets.py:104, barbican:barbican/api/controllers/secretmeta.py:85request [observed] pyapi-secret-metadata; refusal read in the code405 expected [inferred]; no command uses it. Its unit tests pass against a mock of the same wrong route (barbicanclient/tests/v1/test_secrets.py:692)
D25**ACLManager.total() asks GET /acl**, since it inherits total with the entity aclbarbicanclient/base.py:73, barbicanclient/v1/acls.py:456[observed] pyapi-acl-totalno such route in the service; no command uses it
D26The tests that go through the shell ignore its result. self.barbican.run(argv=argv) returns an exit code, and the test only fails on an exception, which cliff has already caught. test_v2_auth fails with Invalid client version v1 and test_v3_auth with a missing project argument, and both pass. No take_action runs in 254 testsbarbicanclient/tests/keystone_client_fixtures.py:252[observed] by wrapping every take_action, and by running these testsnone on a service; it is why D7, D9, D11, D12 and D13 went unseen

Checked and not found

  • **A return value from command.Command, read by cliff as the exit code**: none of the 7 classes returns (barbicanclient/barbican_cli/v1/secrets.py:38, barbicanclient/barbican_cli/v1/secrets.py:106, barbicanclient/barbican_cli/v1/secrets.py:225, barbicanclient/barbican_cli/v1/secrets.py:249, barbicanclient/barbican_cli/v1/orders.py:69, barbicanclient/barbican_cli/v1/containers.py:33, barbicanclient/barbican_cli/v1/acls.py:92).
  • List responses without their key: tolerated, by .get(key, []) (barbicanclient/v1/secrets.py:680, barbicanclient/v1/containers.py:729, barbicanclient/v1/orders.py:448, barbicanclient/v1/cas.py:240): an empty table, exit 0 [observed: 200 probe]. This differs from the SDK-based commands of OSC.

Source: reverse-spec-python-barbicanclient/02-errors_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.