odysseus-dev/odysseus · warning · HTTPException

Password is required

Error message

Password is required

What it means

Raised as HTTP 400 by POST /config/accounts when body.get('password') is falsy. The route requires a non-empty password for every new CalDAV account because CalDAV servers authenticate per-request; there is no anonymous-account path, so an account row without credentials would be unusable.

Source

Thrown at routes/calendar_routes.py:928

            })
        return {"accounts": safe}

    @router.post("/config/accounts")
    async def add_caldav_account(request: Request):
        """Add a new CalDAV account."""
        import uuid as _uuid
        owner = _require_user(request)
        try:
            body = await request.json()
        except Exception:
            body = {}
        from src.caldav_sync import validate_caldav_url
        try:
            url = validate_caldav_url(body.get("url", ""))
        except ValueError as e:
            raise HTTPException(400, str(e))
        if not body.get("password"):
            raise HTTPException(400, "Password is required")
        from src.secret_storage import encrypt
        new_acc = {
            "id": str(_uuid.uuid4()),
            "label": (body.get("label") or "").strip() or "CalDAV",
            "url": url,
            "username": (body.get("username") or "").strip(),
            "password": encrypt(body["password"]),
        }
        accounts = _get_caldav_accounts(owner)
        accounts.append(new_acc)
        _save_caldav_accounts(owner, accounts)
        return {"ok": True, "id": new_acc["id"]}

    @router.put("/config/accounts/{account_id}")
    async def update_caldav_account(account_id: str, request: Request):
        """Update an existing CalDAV account by id."""
        owner = _require_user(request)
        try:

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Include a non-empty 'password' string in the JSON body: {'url': ..., 'username': ..., 'password': '...'} with Content-Type: application/json.
  2. Make the password field required in the UI and disable the submit button until it is non-empty.
  3. If the password is definitely filled in, verify the request body is valid JSON — the route's silent body={} fallback makes any JSON parse failure look like a missing password or URL.

Example fix

# before
curl -X POST /config/accounts -d '{"url": "https://caldav.example.com/", "username": "u"}'

# after
curl -X POST /config/accounts -H 'Content-Type: application/json' \
  -d '{"url": "https://caldav.example.com/", "username": "u", "password": "secret"}'
Defensive patterns

Strategy: validation

Validate before calling

if (!form.password || !form.password.trim()) {
  showError('Password is required');
  return;
}
await api.post('/config/accounts', {url: form.url, username: form.username, password: form.password});

Prevention

When it happens

Trigger: POST /config/accounts with {'url': 'https://...', 'username': 'u'} (no password key); password set to '' or null; JSON body missing entirely so the except branch sets body={} and the password check fails after URL validation passes.

Common situations: Frontend 'Add account' form not marking the password field required; password field bound to an empty state variable on first render; client sending multipart/form-data which request.json() cannot parse, collapsing body to {}; user pasting only whitespace.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/01ac3cd4eb710aa9. Report an issue: GitHub.