Skip to content

Empty callback exception messages prevent activity failure reporting #222

Open

Description

@rmcdaniel

A callback exception with an empty message cannot be reported as a failed activity. For example, raise AssertionError() reaches the worker's error handler, which sends failure.message: ""; Server rejects it with 422 / The failure.message field is required. The worker logs that reporting failed, and a standalone activity remained running through the caller's 30-second result deadline.

Reproduced during Python 2.12.0 unpublished candidate qualification at 105e174a6cc5ef855f006fff36afc5526c4862e8 against published Server 2.11.4 (sha256:9dd7e5fc8382c0bbdda106ffac86ab8edc4264545e2df3c7e43209884fb8091d). The same message=str(e) activity-failure reporting path is present in published 2.11.2 source 43be84a9e081141db9ec4af0eca4cdc245dd6efb; a separate published-2.11.2 runtime reproduction is not claimed.

Use a useful non-empty fallback for blank exception messages, preserving the actual failure type, original attempt/lease identity, stack and bounded failure-report contract. Cover ordinary and supervised callbacks, whitespace-only messages and a real connected activity: one failed completion must be accepted and the caller must receive a terminal typed failure rather than an unexplained timeout. Check the corresponding local/workflow failure paths for the same report-shape problem.

This is a separate pre-existing failure-reporting defect, not a regression or a new gate on the original-context capability in #221 / shared250. #167 addressed empty transport diagnostics and #100 accepted update failures; neither owns this worker-to-Server empty callback failure.

Activity

  1. rmcdaniel commented on Oct 27, 2026

    @rmcdaniel
    MemberAuthor

    General accepted triage and implementation after the active Server bridge/admission slice. Preserve failure type, attempt/lease identity and reporting bounds; cover ordinary/supervised blank and whitespace-only exceptions plus a connected terminal-failure result. This remains separate from the delivered 2.12.0 activity-context feature.

  2. rmcdaniel commented on Oct 27, 2026

    @rmcdaniel
    MemberAuthor

    General implemented the repair at 1b4c66b on fix/222-empty-callback-failure.

    A shared exception-message helper preserves nonblank diagnostic text exactly and uses the original exception type when the message is empty or whitespace-only. Ordinary and supervised activity callbacks, invocable handlers, workflow/update failures and query refusals use it. Local activity reporting already has a nonblank fallback. Failure type/class, stack, retry classification and the original task/attempt/lease authority remain unchanged; existing report bounds still apply.

    Qualification on Python 3.12:

    • Full unit suite: 3,613 passed, two skipped, with 125 integration cases deselected.
    • Full Ruff and strict mypy pass (39 source files).
    • Focused callback/process/invocable/bounded-report checks: 82 passed.
    • Connected, unmodified published Server 2.11.4, index sha256:9dd7e5fc8382c0bbdda106ffac86ab8edc4264545e2df3c7e43209884fb8091d, local amd64 config sha256:d925129892b0bc89e7c2f01c57a100afc217d46ee42b68ae3ffefc86bb71d89d: four cases passed (ordinary/supervised × empty/whitespace). Each submits one accepted HTTP 200 failure completion, preserves attempt/lease fields and stack/type, and the caller receives terminal WorkflowFailed instead of timeout.
    • The unchanged baseline source fails that real connected ordinary-empty case: failure reporting gets HTTP 422 failure.message required; the activity remains running until the 15-second result timeout. The focused old-code controls fail 13 cases and pass eight nonblank/existing-fallback controls.

    This is source-candidate evidence, not a published SDK patch or installed-package completion. Normal PR/release and fresh PyPI-installed acceptance remain. The runtime fixture containers/network/database will be removed after retaining this receipt; the source worktree and isolated test environment remain for delivery.

  3. rmcdaniel commented on Oct 27, 2026

    @rmcdaniel
    MemberAuthor

    Additional qualification at source 5525976: the existing replay-corpus policy correctly required durable evidence for the workflow failure-message change. Added blank-workflow-failure-message.json and its real replay workflow. All 66 replay corpus tests pass; the same case fails on unchanged baseline source because it emits whitespace instead of AssertionError. The normal corpus guard passes with nine source/test paths, replay fixtures69→70 and codec fixtures unchanged42. Full Ruff still passes. The earlier full-unit, strict-mypy and four real connected results remain valid for the unchanged application source. Runtime containers/network/database were removed after evidence.

    Next: normal PR/patch publication and fresh installed-PyPI acceptance. The source receipt does not claim a published fix.

  4. rmcdaniel commented on Nov 2, 2026

    @rmcdaniel
    MemberAuthor

    Source follow-through now lives in draft #226 at baf3845, handed over by General. It carries 1b4c66b and 5525976 onto current main; the only conflict was the replay corpus workflow list. It also routes the remaining query and update-validator rejection paths through the same helper, because str(exc) or fallback let whitespace-only messages through. On this head: unit 3,674 passed, with 2 environment-only boundary-scanner failures (macOS bash 3.2); ruff and mypy pass; the corpus guard passes; the blank-failure corpus counterfactual still fails on the main base. Connected/container qualification hasn't been run on this head yet.

  5. rmcdaniel commented on Nov 2, 2026

    @rmcdaniel
    MemberAuthor

    Independent Security provenance verification for published Python SDK 2.12.3 is complete; no finding.

    • Annotated tag object fe3a65e5ff15e2f420e162979c8bac5d6a6fb5cb names 2.12.3 and points to protected squash merge c67353cad41eb0323819fc2a4b362b331c821889. The merge tree b2ed5444dbd24684a286f76aa8c3b41acb1212d5 is byte-identical to accepted PR head bfd48bd7213dbb7ef893886a0d386b3b099b48a4.
    • Publisher run 41469536711 is a successful tag-push run at that exact commit. Its build and publish jobs succeeded. The privileged job downloaded and validated producer artifact 13152869822, SHA-256 a2c4bb2d3da54781185b006bc71652cdbf894baabf9db8c8388e11814120aba6, from the same run/attempt before extraction and OIDC publication.
    • PyPI now serves exactly two files. The wheel is 320,950 bytes, SHA-256 f509643cce6bff4be695d70bc8e6315b7dbd9a0a454ff5d83dd4c43a1c3f7f03; the sdist is 629,181 bytes, SHA-256 f8b4c3c13ea2b800b4dc49881023bfd8cddccb9e3a2cf61e3c0b58a0df25f76a. Both are byte-identical to GitHub build artifact dist from the publisher run.
    • The official PyPI integrity endpoint returns a publish attestation for each file from GitHub publisher durable-workflow/sdk-python, workflow publish.yml, environment pypi. Each in-toto subject name and SHA-256 matches the downloaded file and PyPI JSON exactly.
    • The wheel has 46 entries; every non-RECORD entry hash/size verifies against RECORD, with no unsafe path. All 40 packaged SDK files are byte-identical to the tagged source. The sdist has no link/device/unsafe-path member; its 40 SDK files and all 118 included tests match source, and its pyproject.toml, README and license match. Wheel and sdist metadata agree on name/version, Python floor, dependencies and project URLs.

    The annotated Git tag itself is unsigned; this release provenance is therefore the exact-source protected-main/publisher-handoff/PyPI trusted-publisher and attestation chain, not a signed-tag claim. That matches the current release design and is not a new hold. General remains responsible for fresh registry-installed behavior against the published Server artifacts. No PHP Server release is involved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions