Skip to content

supervisor/web_workflow: check the listener socket calls for failure - #64

Closed
mikeysklar wants to merge 3 commits into
mainfrom
fix/web-workflow-listener-errors
Closed

supervisor/web_workflow: check the listener socket calls for failure#64
mikeysklar wants to merge 3 commits into
mainfrom
fix/web-workflow-listener-errors

Conversation

@mikeysklar

Copy link
Copy Markdown
Owner

What

supervisor_start_web_workflow() created the listening socket, bound it and called listen() without checking any of them, with an explicit // Bind to any ip. (Not checking for failures) comment on the bind. On a port whose network stack can refuse to create a socket, the descriptor stays unset, bind() and listen() run on -1 and fail with EBADF, and the function still returns true.

The caller takes that as success and installs the background callback, so the supervisor polls a socket that was never opened. Nothing is served and there is no diagnostic.

Why

This is shared supervisor code, so it affects every port that has web workflow: espressif, raspberrypi and zephyr-cp. CIRCUITPY_WEB_WORKFLOW ?= $(CIRCUITPY_WIFI), so atmel-samd, stm and nordic do not compile this file and are unaffected.

Not specific to the port where it was found. adafruit#10054 reports web workflow unreachable after a watchdog reset on several ESP32 boards, and the attempted fix in adafruit#10948 moved port 80 from closed to filtered, that is bound but never serving, which is the same end state this produces.

Hardware tested

Silicon Labs SiWx917-DK2605A (BRD2605A), zephyr-cp port, CircuitPython 10.3.0-alpha.4, host on the same LAN. Serial output from the failing case:

socket() ok=0 num=-1 errno=107   ENOTCONN
bind port 80 -> 9 errno=9        EBADF
listen -> 0 errno=9 closed=1
(returns true)

With the change the workflow either starts or reports failure, rather than reporting success on a socket that was never opened. Verified serving afterwards: /cp/version.json 200, /fs/ 401 without credentials and 200 with, PUT 201, GET 200, DELETE 204.

Also built for renesas_ek_ra8d1, a zephyr-cp board CI builds today.

Not tested on hardware: espressif or raspberrypi boards, which is where the corroborating reports in adafruit#10054 come from. Worth doing before this goes upstream, and I have an ESP32 and RP2040 targets on a test rig for it.

Scope

Error checking only. Deliberately not a retry: the next attempt is the next supervisor_workflow_reset(). Bringing the workflow up once the interface acquires an address later is a separate change and is not attempted here.

AI assistance

Written with Claude Code. I ran the hardware and the serial output above is from the board, not from a model.

check_suite["workflowRun"] is null for check suites that are not attached to a
workflow run, for example one that was deleted or one belonging to an app
integration. get_commit_depth_and_check_suite() dereferenced it
unconditionally:

    TypeError: 'NoneType' object is not subscriptable

which fails the scheduler job's Get-last-commit-with-checks step. Skip those
entries rather than indexing into them.
@mikeysklar mikeysklar closed this Aug 17, 2026
@mikeysklar mikeysklar reopened this Aug 17, 2026
dhalbert and others added 2 commits August 17, 2026 18:31
…wrun

tools: skip check suites with no workflowRun in ci_changes_per_commit.py
start_web_workflow() created the listening socket, bound it and called
listen() without checking any of them, with an explicit "(Not checking for
failures)" comment on the bind. On a port whose network stack can refuse to
create a socket, the descriptor stays unset and bind() and listen() then run
on -1 and fail with EBADF, but the function still returns true. The caller
takes that as success and installs the background callback, so the supervisor
polls a socket that was never opened and the board can hang before running
code.py. Nothing is ever served, and there is no diagnostic.

Observed on a Silicon Labs SiWx917-DK2605A (BRD2605A) running the zephyr-cp
port, CircuitPython 10.3.0-alpha.4, where socket() returns ENOTCONN until the
interface has an address:

    socket() ok=0 num=-1 errno=107   ENOTCONN
    bind port 80 -> 9 errno=9        EBADF
    listen -> 0 errno=9 closed=1
    (returns true)

Return false instead of reporting success, and close the socket if bind or
listen is the step that failed. Note this is not a retry: the next attempt is
the next supervisor_workflow_reset(). Bringing the workflow up once the
interface acquires an address later is a separate change.

This is not specific to that port. adafruit#10054 reports web workflow unreachable
after a watchdog reset on several ESP32 boards, and the attempted fix in
 adafruit#10948 moved port 80 from closed to filtered, i.e. bound but never serving,
which is the same end state this produces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mikeysklar
mikeysklar force-pushed the fix/web-workflow-listener-errors branch from af20412 to 444e5f9 Compare August 17, 2026 22:42
@mikeysklar

Copy link
Copy Markdown
Owner Author

Superseded by adafruit#11207, which targets adafruit:main directly. Closing this one so there is a single thread for the fix.

@mikeysklar mikeysklar closed this Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants