pathwaycom/pathway · error · ValueError

Trying to modify a row in {type(self)} but deletions_enabled

Error message

Trying to modify a row in {type(self)} but deletions_enabled is set to False.

What it means

Raised by Pathway's Python input connector when a custom ConnectorSubject emits a modification (an upsert of an existing key) through a session of SessionType.UPSERT while deletions_enabled=False was passed to pw.io.python.read. With deletions disabled the engine assumes an append-only stream, so an upsert-style rewrite of a key is a contract violation and is rejected instead of silently dropping or corrupting state.

Source

Thrown at python/pathway/io/python/__init__.py:272

    ) -> dict[str, Any]:
        match self._pw_format:
            case "json":
                values = json.loads(message.decode(encoding="utf-8"))
            case "raw":
                values = {"data": message.decode(encoding="utf-8")}
            case _:
                assert self._pw_format == "binary"
                values = {"data": message}
        if metadata is not None:
            values["_metadata"] = json.loads(metadata.decode(encoding="utf-8"))
        return values

    def _add_inner(self, key: Pointer | None, values: dict[str, Any]) -> None:
        if self._session_type == SessionType.NATIVE:
            self._buffer.put((PythonConnectorEventType.INSERT, key, values))
        elif self._session_type == SessionType.UPSERT:
            if not self._deletions_enabled:
                raise ValueError(
                    f"Trying to modify a row in {type(self)} but deletions_enabled is set to False."
                )
            self._buffer.put((PythonConnectorEventType.INSERT, key, values))
        else:
            raise NotImplementedError(f"session type {self._session_type} not handled")

    def _remove(
        self, key: Pointer, message: bytes, metadata: bytes | None = None
    ) -> None:
        self._remove_inner(key, self._get_values_dict(message, metadata))

    def _remove_inner(self, key: Pointer | None, values: dict[str, Any]) -> None:
        if not self._deletions_enabled:
            raise ValueError(
                f"Trying to delete a row in {type(self)} but deletions_enabled is set to False."
            )
        self._buffer.put((PythonConnectorEventType.DELETE, key, values))

View on GitHub (pinned to fa2f74a464)

Solutions

  1. Set deletions_enabled=True (default) in pw.io.python.read so updates to an existing key are legal.
  2. Or make the subject truly append-only: never reuse a key in next(); derive unique keys per event (e.g. include a sequence number or timestamp).
  3. If only late corrections arrive, emit the correction as a new row with a new key and dedupe downstream with reducers instead of mutating the key.

Example fix

# before
def run(self):
    self.next("row-1", {"v": 1})
    self.next("row-1", {"v": 2})  # modification of existing key
pw.io.python.read(Subj(), schema=S, deletions_enabled=False)

# after
pw.io.python.read(Subj(), schema=S)  # deletions_enabled=True allows upserts
Defensive patterns

Strategy: validation

Validate before calling

may_update_keys = True  # set to False only for truly append-only sources
pw.io.python.read(Subj(), schema=S, deletions_enabled=may_update_keys)

Prevention

When it happens

Trigger: Implementing a ConnectorSubject whose run() calls subject.next(key, values) twice with the same key (the second call is a modify) while the connector was created with pw.io.python.read(subject, ..., deletions_enabled=False); or commit/replay logic that re-emits rows.

Common situations: Feeding an upsert source (e.g. a dict-backed feed or Kafka compacted topic) into a pipeline configured for append-only processing to save memory; porting an existing subject from UPSERT semantics to static mode and forgetting duplicate keys can occur.

Related errors


AI-assisted analysis of pathwaycom/pathway@fa2f74a464 (2026-08-15). Data as JSON: /api/errors/5895d61068599b59. Report an issue: GitHub.