passbolt/passbolt_api · error · BadRequestException

V4 related fields are not supported for V5.

Error message

V4 related fields are not supported for V5.

What it means

MetadataResourceDto::validateRequestPayload rejects resource payloads that mix v5 metadata fields with non-null v4 cleartext fields (name, username, uri, description). Once the payload is identified as v5, any of those legacy fields present triggers this BadRequestException. It enforces that v5 resources carry only encrypted metadata, not both formats.

Solutions

  1. Move the cleartext fields (name, username, uri, description) inside the encrypted `metadata` blob and remove them from the top-level payload.
  2. Send either a pure v4 payload (only name/username/uri/description) or a pure v5 payload (only metadata/metadata_key_id/metadata_key_type).
  3. Upgrade the client/SDK to build v5 metadata: encrypt the cleartext object (object_type PASSBOLT_RESOURCE_METADATA) with the metadata key.
  4. If v5 usage is unintended, drop the metadata fields from the request so the DTO treats it as v4.

Example fix

// before
{"name": "GitHub", "username": "ops@acme", "metadata": "<encrypted>", "metadata_key_id": "<uuid>", "metadata_key_type": "shared_key"}

// after
{"metadata": "<encrypted blob containing name/username/uris/description>", "metadata_key_id": "<uuid>", "metadata_key_type": "shared_key"}
Defensive patterns

Strategy: validation

Validate before calling

$v4Fields = ['name', 'username', 'uri', 'description'];
$isV5 = isset($payload['metadata']) && $payload['metadata'] !== null;
if ($isV5) {
    foreach ($v4Fields as $f) {
        if (array_key_exists($f, $payload) && $payload[$f] !== null) {
            throw new InvalidArgumentException("v5 payload must not contain v4 field: $f");
        }
    }
}

Type guard

function hasNoV4FieldsWithV5(array $p): bool {
    if (!isset($p['metadata']) || $p['metadata'] === null) return true;
    foreach (['name','username','uri','description'] as $f) {
        if (array_key_exists($f, $p) && $p[$f] !== null) return false;
    }
    return true;
}

Try / catch

try {
    $dto = MetadataResourceDto::fromArray($payload);
} catch (BadRequestException $e) {
    // move name/username/uri/description into the encrypted metadata blob and retry
}

Prevention

When it happens

Trigger: POST/PUT to resource endpoints with a payload containing metadata/metadata_key_id/metadata_key_type plus any of name, username, uri, or description (non-null), with the Metadata plugin enabled; constructing MetadataResourceDto::fromArray with mixed-format data.

Common situations: v4-era clients or scripts hitting a v5-enabled server and sending the full legacy resource object with metadata fields appended; integrations that copy a v4 resource and add a `metadata` key; incomplete SDK upgrades that still serialize description/username at the top level.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/01da19bd915114be. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltCe/Metadata/src/Model/Dto/MetadataResourceDto.php:187

            throw new BadRequestException($msg);
        }

        // Now that we know that we have a valid v5 payload, we check that no v4 fields are in the payload
        $v4SuperfluousFields = [];
        foreach (self::V4_META_PROPS as $v4Field) {
            if (array_key_exists($v4Field, $payload) && !is_null($payload[$v4Field])) {
                $v4SuperfluousFields[] = $v4Field;
            }
        }
        if (!empty($v4SuperfluousFields)) {
            $msg = __('V4 related fields are not supported for V5.');
            if (Configure::read('debug')) {
                Log::error($msg);
                Log::error(__('Superfluous fields: {0}', implode(', ', $v4SuperfluousFields)));
            }

            throw new BadRequestException($msg);
        }
    }

    /**
     * Returns metadata array in cleartext form as per v5 format.
     *
     * @param bool $mapResourceType Should map resource type or not.
     * @return array
     */
    public function getClearTextMetadata(bool $mapResourceType = true): array
    {
        $resourceTypeId = $this->data[self::RESOURCE_TYPE_ID];
        if ($mapResourceType) {
            $mapping = ResourceType::getV5Mapping();

            $v4resourceTypeId = $this->data[self::RESOURCE_TYPE_ID];
            if (!isset($mapping[$v4resourceTypeId])) {
                throw new InternalErrorException(__('No resource type mapping for ID \'{0}\'', $v4resourceTypeId));

View on GitHub (pinned to 31c1bbc10f)