multica-ai/multica · warning
key is required
Error message
key is required
What it means
validateIssueMetadataKey rejects a metadata write whose URL-embedded key is the empty string. Issue metadata V1 is a flat key-value store addressed as PUT/DELETE .../metadata/{key}; an empty key means the request path ended in /metadata/ (or the router extracted nothing), which cannot name a value. The empty check runs before the pattern check and gives the distinct, actionable 'required' message.
Source
Thrown at server/internal/handler/issue_metadata.go:47
// any whole-blob overwrite would race with concurrent agent writes (see the
// design discussion on MUL-2017).
const (
maxIssueMetadataKeys = 50
)
var issueMetadataKeyRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_.-]{0,63}$`)
// SetIssueMetadataKeyRequest carries the JSON value to write under the key
// named in the URL. Value is a RawMessage so we can preserve numeric vs.
// string typing through to PostgreSQL — once decoded into `any`, JSON
// numbers all collapse to float64 and we'd lose integer fidelity.
type SetIssueMetadataKeyRequest struct {
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)
}View on GitHub (pinned to 2c0912b6ec)
Solutions
- Require a non-empty key in the client form/UI before enabling submit.
- Check key truthiness before building the request URL.
- Fix route wiring so the {key} path parameter is captured and non-empty (404/routing rather than handler-level empty).
- Trim whitespace and reject empty-after-trim keys.
Example fix
// before
await fetch(`${base}/issues/${id}/metadata/${key}`, {...}); // key may be ''
// after
if (!key || !key.trim()) throw new Error('metadata key is required');
await fetch(`${base}/issues/${id}/metadata/${encodeURIComponent(key.trim())}`, {...}); Defensive patterns
Strategy: validation
Validate before calling
function metadataUrl(base, issueId, key) {
const k = String(key ?? '').trim();
if (!k) throw new TypeError('metadata key is required');
return `${base}/issues/${issueId}/metadata/${encodeURIComponent(k)}`;
} Type guard
const isNonEmptyKey = (k) => typeof k === 'string' && k.trim().length > 0;
Prevention
- Disable the save button until the key field has non-whitespace content.
- Keep key building in one URL helper so the empty check cannot be bypassed.
- Encode the key with encodeURIComponent — spaces and slashes would otherwise corrupt the path.
When it happens
Trigger: PUT /issues/{id}/metadata/ (trailing slash, empty key segment); client building the URL as `${base}/metadata/${key}` with key undefined/empty in JS; a route misconfiguration forwarding /metadata directly to the handler without a key capture group.
Common situations: Frontend form allowing submit with an empty key field; template string with an unset variable producing .../metadata/; proxy/gateway rewriting that strips the last path segment.
Related errors
- key must match ^[a-zA-Z_][a-zA-Z0-9_.-]{0,63}$
- value is required
- 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/ec1ade1e98acbe98.
Report an issue: GitHub.