Problem
Every resource write path (vis/group/job/repo — the create_* PUT and api_write POST) handles 404/403/409 explicitly but, on any other non-ok response, prints the body + headers + the literal "should raise a general error" and returns. The caller gets no exception and the write silently no-ops.
This surfaced via novem-code/gaia#2656: a paying user runs mail.bcc = [list]; when the list exceeds the subscription's external-address cap the server rejects the write, but the SDK swallows the response — mail.bcc = … returns with nothing stored and nothing to catch. (The server now returns a proper 400 with {message, rejected:[{line, reason}]}; the SDK still drops it on the floor.)
Minimal repro (novem 0.6.0, premium account, cap 25):
from novem import Mail
m = Mail("x", create=True)
m.bcc = [f"u{i}@example.com" for i in range(1, 31)] # 30 > cap
# prints a blob incl. "should raise a general error", returns normally, stores 0
The tension: fail soft vs fail hard
Making the SDK raise on non-2xx is more correct, but it's a behavioral change for existing users, so it's worth a deliberate call:
- Fail hard (raise): surfaces real errors (the #2656 case), but scripts that previously "succeeded" on a partial/failed write now get an exception — could break pipelines that quietly tolerated drops.
- Fail soft (today): never raises on these paths → silent data loss, no signal. This is the reported bug.
Open questions:
- Raise on any non-ok, or only on 4xx (keep 5xx softer / retryable)?
- Keep the existing 409 carve-out (creating an object that already exists is intentionally a no-op).
- Do we want an opt-in/opt-out (
raise_on_error/strict flag, or a soft default + deprecation window) to avoid breaking existing users on a minor release?
- Does this warrant a major version bump given the contract change?
Proposed direction
A shared raise_on_response() that surfaces the server message + any rejected lines and raises the matching NovemException subclass, wired into all write sites (keeping the 409 carve-out). Draft PR incoming.
Problem
Every resource write path (
vis/group/job/repo— thecreate_*PUT andapi_writePOST) handles 404/403/409 explicitly but, on any other non-ok response, prints the body + headers + the literal"should raise a general error"and returns. The caller gets no exception and the write silently no-ops.This surfaced via
novem-code/gaia#2656: a paying user runsmail.bcc = [list]; when the list exceeds the subscription's external-address cap the server rejects the write, but the SDK swallows the response —mail.bcc = …returns with nothing stored and nothing to catch. (The server now returns a proper400with{message, rejected:[{line, reason}]}; the SDK still drops it on the floor.)Minimal repro (novem 0.6.0, premium account, cap 25):
The tension: fail soft vs fail hard
Making the SDK raise on non-2xx is more correct, but it's a behavioral change for existing users, so it's worth a deliberate call:
Open questions:
raise_on_error/strictflag, or a soft default + deprecation window) to avoid breaking existing users on a minor release?Proposed direction
A shared
raise_on_response()that surfaces the servermessage+ anyrejectedlines and raises the matchingNovemExceptionsubclass, wired into all write sites (keeping the 409 carve-out). Draft PR incoming.