Skip to content

sessionValidator mutates the frozen session record on Harper v5 — "Cannot assign to read only property 'lastValidated'" #222

Description

@heskew

Summary

On Harper v5, the periodic token-validation path for non-expiring tokens throws on every validation attempt:

[error] Token validation error: Cannot assign to read only property 'lastValidated' of object '#<Object>'

validateAndRefreshSession mutates the session record's nested oauth object in place (src/lib/sessionValidator.ts:88):

oauthMetadata.lastValidated = now;
session.oauth = oauthMetadata;

Harper v5 freezes record values on the read path (resources/Table.tsObject.freeze(value); also resources/tracked.ts), so session.oauth arrives frozen and the assignment throws. Harper v4 never froze records, which is why this only surfaces on v5.

Impact

  • The throw happens after a successful validateToken call but before lastValidated is persisted, and the surrounding catch swallows it — so validation "succeeds" but the timestamp never advances.
  • Once a session is past tokenValidationInterval (default 15 min), every request re-runs the provider validateToken HTTP call: error-log spam plus one provider API call per request (rate-limit exposure for GitHub-style providers).
  • No functional session breakage: sessions stay valid, and revoked-token detection still works (it runs more often than intended, not less).

Observed live during a production Harper 4.7 → 5.2.6 upgrade of an internal deployment (2026-09-01), spamming on both nodes of the pair.

Affected versions

The in-place mutation is present in every 2.x release (v2.0.0 through v2.5.0 and current main) and in the 1.x line. In practice only 2.x-on-v5 matters: 1.x targets Harper 4, which doesn't freeze records. Only the non-expiring-token branch is affected — the token-refresh path already builds a fresh updatedMetadata object.

Fix

Build a new object instead of mutating the frozen one:

session.oauth = { ...oauthMetadata, lastValidated: now };

Worth a quick audit at the same time for any other in-place mutation of session.* nested objects (e.g. in clearOAuthSession) — any of them will throw the same way on v5 frozen records.

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Fields

Priority

P1

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions