Submission with `is_public=True` returns HTTP 500 instead of being accepted - apps/jobs/views.py:365
- Dominant language
- Python
- Stars
- 2k
- Forks
- 984
- Avg merge
- 2h 54m
- Merged PRs (30d)
- 14
Description
Submitting with `is_public=True` — Python's own spelling of the boolean — returns HTTP 500. Only the lowercase JSON spellings `true`/`false` are accepted.
## Offending code
`challenge_submission()` parses the form-encoded `is_public` flag with `json.loads()`:
https://github.com/Cloud-CV/EvalAI/blob/f8aff86a7e613c61fae3e4b0e5c0382fe3b52a25/apps/jobs/views.py#L360-L365
```python
if request.data.get("is_public") is None:
request.data["is_public"] = (
True if challenge_phase.is_submission_public else False
)
else:
request.data["is_public"] = json.loads(request.data["is_public"])
```
The same line appears in `get_submission_file_presigned_url()`:
https://github.com/Cloud-CV/EvalAI/blob/f8aff86a7e613c61fae3e4b0e5c0382fe3b52a25/apps/jobs/views.py#L3050
## Why it is wrong
`is_public` is a `BooleanField` on the model, and these are form-encoded (`multipart`) requests, so the value arrives as a **string**. `json.loads()` only accepts the JSON spellings, which is a much narrower set than what clients actually send:
| value sent | `json.loads()` result |
|-----------------|--------------------------------|
| `"true"` | `True` |
| `"false"` | `False` |
| `"True"` | **JSONDecodeError → HTTP 500** |
| `"False"` | **JSONDecodeError → HTTP 500** |
| `"on"` (HTML checkbox) | **JSONDecodeError → HTTP 500** |
| `"yes"` | **JSONDecodeError → HTTP 500** |
| `""` | **JSONDecodeError → HTTP 500** |
| `True` (JSON body, real bool) | **TypeError → HTTP 500** |
`json.JSONDecodeError` subclasses `ValueError`, not `APIException`, and the call sits outside every `try` block in both views. DRF's `exception_handler` returns `None` for it, so it re-raises into Django and the request is answered with HTTP 500:
```
>>> from rest_framework.views import exception_handler
>>> import json
>>> try: json.loads("True")
... except Exception as e: exc = e
>>> exception_handler(exc, {})
None # -> re-raised -> HTTP 500
```
This is also narrower than the serializer that ultimately receives the value. DRF's `BooleanField` already accepts `True`, `true`, `on`, `yes`, `y`, `t`, `1` (and the falsy equivalents) and raises a clean `ValidationError` for anything else, so the `json.loads()` call is doing the same job less correctly.
Note the web UI is unaffected: `challengeCtrl.js` appends a JS boolean to `FormData`, which stringifies to lowercase `"true"`/`"false"`. The bug is reached by API and CLI clients, where writing `True` in Python is the natural thing to do.
## What the user sees
- A submission sent with `is_public=True` from a Python client fails with an opaque `500 Internal Server Error` rather than being accepted.
- An HTML form posting a checkbox (`is_public=on`) gets a 500.
- Genuinely invalid values such as `is_public=banana` also produce a 500 rather than a validation error naming the field.
- Every one of these is reported to Sentry as a server fault, adding noise that hides real server errors.
## Steps to reproduce
1. Authenticate as a participant in an active challenge phase.
2. `POST` to `/api/jobs/challenge//challenge_phase//submission/` as `multipart/form-data` with:
```
status=submitting
input_file=
is_public=True
```
3. The response is HTTP 500 with an unhandled `json.decoder.JSONDecodeError`.
Repeating with `is_public=true` succeeds, which shows the divergence.
## Expected behaviour
- `is_public` accepts the same boolean spellings the `Submission` serializer accepts for that field (`True`/`true`/`on`/`1`/`yes` and falsy equivalents).
- A value that is not a recognised boolean returns HTTP 400 naming `is_public`.
- No unhandled exception is raised, so these are not reported to Sentry as server errors.
- `challenge_submission()` and `get_submission_file_presigned_url()` behave the same way.
---
This is the same class of defect as #5222 but on a different code path and field; it was found while investigating that one. I have a branch that routes both call sites through DRF's `BooleanField` and adds tests, and will open a PR referencing this issue.
Contributor guide
Research direction
Start in apps/jobs/views.py at the parsing code in challenge_submission() and get_submission_file_presigned_url(), then reproduce the multipart request with is_public=True and the other listed spellings. Compare those paths with the Submission serializer's BooleanField behavior. Done means both endpoints accept recognized boolean values and return a field-level 400 for invalid values without an unhandled exception.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- django, python
- Domain
- api, backend, testing
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100