Skip to content

Execution.ts overwrites a failed JS/PY/R session's state back to completed #390

Description

@YuryShkoda

Description

For the JS/PY/R runtimes, processProgram correctly sets
session.state = SessionState.failed (with session.failureReason set)
when the spawned interpreter process exits non-zero:

https://github.com/sasjs/server/blob/0af6b63/api/src/controllers/internal/processProgram.ts#L121-L131

await execFilePromise(executablePath, [codePath], writeStream)
  .then(() => {
    session.state = SessionState.completed
    ...
  })
  .catch((err) => {
    session.state = SessionState.failed
    session.failureReason = err.toString()
    ...
  })

Unlike the SAS runtime, this branch doesn't throw on failure — it just sets
the state and returns normally. Immediately after processProgram returns,
ExecutionController.executeProgram unconditionally overwrites that state:

https://github.com/sasjs/server/blob/0af6b63/api/src/controllers/internal/Execution.ts#L146-L147

// it should be deleted by scheduleSessionDestroy
session.state = SessionState.completed

So a JS/PY/R session that just failed has its failed state silently
stomped back to completed before the session is ever cleaned up or
reused.

Impact

  • session.failureReason is still returned correctly in the HTTP response
    (that's read from the log/webout files, not from session.state), so
    this does not affect the client-visible error response for a single
    request.
  • The impact is on the session object's own bookkeeping: anything that
    later inspects session.state (e.g. scheduleSessionDestroy in
    Session.ts:204-236, which branches on state !== SessionState.completed
    for expiresAfterMins handling) sees a crashed session mis-reported as
    successfully completed.
  • Scoped to JS/PY/R only. For the SAS runtime this can no longer happen:
    since Stored Programs fail to respond in API when SAS errors #388's fix, a failed SAS session throws out of processProgram
    before Execution.ts:147 is ever reached.

Suggested fix

Only set completed if the session didn't already fail, e.g.:

if (session.state !== SessionState.failed) {
  session.state = SessionState.completed
}

Context

Found as a related-but-out-of-scope finding while investigating #388
("Stored Programs fail to respond in API when SAS errors"). Not fixed
there to keep that PR scoped to the SAS hang; tracked here as a follow-up.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingreleased

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions