multica-ai/multica · warning

value is required

Error message

value is required

What it means

validateIssueMetadataValue rejects a metadata write whose JSON body has no `value` field content at all — len(raw) == 0 means the request either had an empty body or a literal JSON "value": null was collapsed to nothing by decoding (Go's json RawMessage keeps "null" as bytes, so in practice this is an absent/empty body or a decode upstream dropped it). The metadata V1 surface requires exactly one primitive value per write; it cannot represent 'no value'.

Source

Thrown at server/internal/handler/issue_metadata.go:60

	Value json.RawMessage `json:"value"`
}

func validateIssueMetadataKey(key string) error {
	if key == "" {
		return errors.New("key is required")
	}
	if !issueMetadataKeyRE.MatchString(key) {
		return errors.New("key must match ^[a-zA-Z_][a-zA-Z0-9_.-]{0,63}$")
	}
	return nil
}

// validateIssueMetadataValue rejects anything other than a primitive JSON
// scalar. Null, arrays, and objects are not allowed — the V1 surface is
// flat KV. Removing a key uses DELETE, not a null value.
func validateIssueMetadataValue(raw json.RawMessage) error {
	if len(raw) == 0 {
		return errors.New("value is required")
	}
	var v any
	if err := json.Unmarshal(raw, &v); err != nil {
		return fmt.Errorf("value must be valid JSON: %w", err)
	}
	switch v.(type) {
	case string, bool, float64:
		return nil
	case nil:
		return errors.New("value cannot be null (use DELETE to remove a key)")
	default:
		return errors.New("value must be a primitive: string, number, or bool")
	}
}

// parseIssueMetadata decodes the JSONB bytes from db.Issue.Metadata into a
// Go map suitable for response serialization. Empty or unparseable blobs
// degrade to an empty map — the DB CHECK guarantees object shape, so this

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Always send a JSON body containing value: {"value": <string|number|bool>}.
  2. Use DELETE /issues/{id}/metadata/{key} to remove a key — never an empty PUT.
  3. Guard client-side: if (value === undefined) abort or default it explicitly.
  4. Verify proxies/middleware are not consuming or dropping the request body before it reaches the handler.

Example fix

// before
await fetch(url, {method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({})});

// after
await fetch(url, {method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({value: 'some string'})});
Defensive patterns

Strategy: validation

Validate before calling

async function putMetadata(url, value) {
  if (value === undefined) throw new TypeError('metadata value is required (use DELETE to remove)');
  const res = await fetch(url, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ value }),
  });
  if (!res.ok) throw new Error(`metadata write failed: ${res.status}`);
}

Type guard

const hasPrimitiveValue = (body) => body !== null && typeof body === 'object' && 'value' in body && body.value !== undefined;

Prevention

When it happens

Trigger: PUT /issues/{id}/metadata/{key} with an empty request body, or a body of '{}' (no value key), or Content-Length 0 from a misconfigured client. Deletion has a dedicated DELETE verb, so an empty PUT is always a client bug.

Common situations: fetch POST/PUT with a body forgotten; client serializing {value: undefined} which JSON.stringify drops to '{}'; proxy stripping the body; using PUT with empty body intending deletion instead of DELETE.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/199f1ced3a6fff88. Report an issue: GitHub.