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

After confirming the payload is a valid V5 payload (all V5 metadata fields present), MetadataTagDto::validate() rejects any V4 field (currently 'slug') that is non-null, throwing this 400 BadRequestException. V4 and V5 representations are mutually exclusive in one request to prevent ambiguous writes.

Solutions

  1. Remove 'slug' (set it to null / omit the key) when sending V5 metadata fields — the slug lives inside the encrypted metadata.
  2. Audit serializers/DTO mappers so V4 props are stripped when a V5 payload is built.
  3. If you need the plain-text slug, put it in the metadata's clear-text object (PASSBOLT_TAG_METADATA.slug) before encryption.
  4. Send a pure V4 payload ({slug} only) if the Metadata plugin is not enabled.

Example fix

// before: mixed payload
POST /tags { "slug": "my-tag", "metadata": "<encrypted>", "metadata_key_id": "<uuid>", "metadata_key_type": "shared_key" }
// after: v5 only
POST /tags { "metadata": "<encrypted>", "metadata_key_id": "<uuid>", "metadata_key_type": "shared_key" }
Defensive patterns

Strategy: validation

Validate before calling

if (body.metadata != null && (body.slug != null)) throw new Error('cannot mix v4 slug with v5 metadata fields');

Type guard

const isMixedV4V5 = (b) => b.metadata != null && b.slug != null;

Try / catch

try { await api.put(`/tags/${id}`, body); } catch (e) { if (e.response?.status === 400 && /V4 related fields/.test(e.response?.data?.message)) body = stripV4Fields(body); throw e; }

Prevention

When it happens

Trigger: Sending both slug and the V5 metadata fields in the same tag create/update body, e.g. {slug: 'my-tag', metadata: ..., metadata_key_id: ..., metadata_key_type: ...}.

Common situations: Clients keeping the old 'slug' key in payloads for backward compatibility while adding V5 fields; generic serializers that always emit every entity property including null-able slug defaults; migrated integrations where slug is defaulted to '' rather than omitted.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at plugins/PassboltEe/Tags/src/Model/Dto/MetadataTagDto.php:195

            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, $data) && !is_null($data[$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.
     *
     * @return array
     */
    public function getClearTextMetadata(): array
    {
        return [
            'object_type' => 'PASSBOLT_TAG_METADATA',
            'slug' => $this->slug,
            // below fields are null for now will be added in future
            'color' => null,
            'description' => null,
            'icon' => null,
        ];

View on GitHub (pinned to 31c1bbc10f)