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 thisView on GitHub (pinned to 2c0912b6ec)
Solutions
- Always send a JSON body containing value: {"value": <string|number|bool>}.
- Use DELETE /issues/{id}/metadata/{key} to remove a key — never an empty PUT.
- Guard client-side: if (value === undefined) abort or default it explicitly.
- 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
- Reject undefined values before serializing — JSON.stringify({value: undefined}) silently yields '{}'.
- Use DELETE for removal; never send an empty-body PUT.
- Verify test/proxy harnesses forward request bodies on PUT.
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
- key is required
- key must match ^[a-zA-Z_][a-zA-Z0-9_.-]{0,63}$
- value cannot be null (use DELETE to remove a key)
- value must be a primitive: string, number, or bool
- invalid limit
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/199f1ced3a6fff88.
Report an issue: GitHub.