siyuan-note/siyuan · error
Fields [mode] and [modeOS] must be provided together
Error message
Fields [mode] and [modeOS] must be provided together
What it means
The bazaar install endpoint accepts an optional legacy pairing of fields: mode (target type: 0 = app, 1 = frontend plugin context) and modeOS (OS-specific flag). These two must always be sent together; sending only one is ambiguous and rejected by the decoder. This preserves compatibility with older clients while enforcing a consistent contract.
Solutions
- Add the missing sibling field so both mode and modeOS are present in the same request.
- Remove both fields entirely if you do not need the legacy mode selection.
- Check the caller's payload-construction code to ensure it sets the pair atomically.
- Consult the current API contract docs for the correct mode/modeOS value pair.
Example fix
// before
{"packageName": "siyuan-plugin-x", "mode": 1}
// after
{"packageName": "siyuan-plugin-x", "mode": 1, "modeOS": false} Defensive patterns
Strategy: validation
Validate before calling
const keys = Object.keys(payload);
if (keys.includes("mode") !== keys.includes("modeOS")) {
throw new Error("mode and modeOS must be sent together");
} Type guard
const hasBothOrNeither = (p) => (("mode" in p) === ("modeOS" in p)); Try / catch
try {
await post("/api/bazaar", payload);
} catch (e) {
if (String(e.message).includes("must be provided together")) {
payload.modeOS = payload.modeOS ?? false;
await post("/api/bazaar", payload);
} else { throw e; }
} Prevention
- Set mode and modeOS in the same code path / single object literal
- Keep payload templates in sync with the current API contract
- Add a shared payload schema test for the bazaar install endpoint
- Remove both fields together when dropping legacy mode selection
When it happens
Trigger: POSTing to the bazaar install endpoint with {"packageName":"x","mode":0} (missing modeOS) or {"packageName":"x","modeOS":false} (missing mode).
Common situations: Partial upgrades of client code where one field was added but its sibling wasn't, hand-written curl payloads copied from outdated docs, or plugin code that sets only modeOS to target a specific OS.
Related errors
- Field [ ] is required
- AI editor action must not be empty
- block [ ] type is locked: expected , got
- block swap requires two non-document blocks
- Bookmark cannot be empty
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/39215cdad81ac5ce.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/bazaar.go:916
if request.Keyword, err = bazaarString(fields, "keyword", false, false); err != nil {
return request, err
}
if request.RepoURL, err = bazaarString(fields, "repoURL", true, true); err != nil {
return request, err
}
if request.RepoHash, err = bazaarString(fields, "repoHash", true, true); err != nil {
return request, err
}
if request.RepoRef, err = bazaarString(fields, "repoRef", false, false); err != nil {
return request, err
}
if request.PackageName, err = bazaarString(fields, "packageName", true, true); err != nil {
return request, err
}
_, hasMode := fields["mode"]
_, hasModeOS := fields["modeOS"]
if hasMode != hasModeOS {
return request, errors.New("Fields [mode] and [modeOS] must be provided together")
}
if hasMode {
mode, parseErr := legacyField[float64](fields, "mode", "Number", true)
if parseErr != nil {
return request, parseErr
}
modeOS, parseErr := legacyField[bool](fields, "modeOS", "Boolean", true)
if parseErr != nil {
return request, parseErr
}
if mode != 0 && mode != 1 {
return request, errors.New("Field [mode] must be 0 or 1")
}
request.Mode = &mode
request.ModeOS = &modeOS
}
return request, nil
}View on GitHub (pinned to 9f775e8a12)