fix(mv_getjoblog): surface actual error when job fails before session creation - #442
fix(mv_getjoblog): surface actual error when job fails before session creation#442sasjs-dev[bot] wants to merge 14 commits into
Conversation
… creation When a Viya JES job fails before a compute session is created (e.g. 403 on POST /compute/contexts/.../sessions), the job response has state=failed and loglocation is missing (SAS missing value '.'). The old code only checked for an empty loglocation, not a missing value, so it fell through to the 'URI is too short - .' error, hiding the real failure reason. This patch: - Also captures the job state from the response - Checks for failed/canceled state OR missing/empty loglocation (incl. '.') - Reads the error dataset from the JES response (httpStatusCode, message) - Reports the real error via mp_abort instead of the opaque message Tested on Viya 2026 (nextviya.emea.sas.com): - Before: MP_ABORT MSG 'URI is too short - .' - After: MP_ABORT MSG 'Job failed, no log available. Error 403: You are not authorized to submit this request.'
There was a problem hiding this comment.
Code Review — 4gl-reviewer
Summary: Good fix — surfaces the actual error from failed Viya compute jobs (e.g. 403 on session creation) instead of the opaque "URI is too short" message. Skills documentation updates are thorough. A few SAS macro issues to address.
Issues:
viya/mv_getjoblog.sas:186—call symputx('jobstate',state,'l')assumes thestatevariable exists in the JSON root dataset. If an older API version or unexpected response omits it, this producesWARNING: Variable state is uninitialized— which the repo's own SAS skill docs (added in this PR) say should be treated as a defect. Consider guarding with a variable-existence check.viya/mv_getjoblog.sas:193—%local jobstate;is declared aftercall symputx('jobstate',state,'l')already creates the variable. While%localis processed at macro compile time (so it works functionally), it violates the "declare locals at the top" convention this repo follows and is confusing to readers.viya/mv_getjoblog.sas:194— The%putstatement executes unconditionally on every call, including successful jobs. Production macros in @sasjs/core typically gate debug output behind a debug flag (e.g.mdebug). Consider removing or guarding it.
Suggestions:
viya/mv_getjoblog.sas:193— Move%local jobstate;(and%local err_httpcode err_msg;on line 198) to the top of the macro alongside existing%localdeclarations, before any data steps that use them.viya/mv_getjoblog.sas:195-197— The outer%ifcatchesjobstate=failed/canceledOR empty loglocation, but the inner%ifonly checks loglocation. Ifjobstate=failedbut a loglocation exists, execution falls through to normal log fetching — this seems intentional (you can still fetch the log). A one-line comment confirming this would help.
Looks good:
- Error extraction from
&libref1..errorwithstop;is clean and correctly avoids reading past the first error row. - Defensive
%str()wrapping on all macro variable comparisons follows safe SAS macro practice. - Skills documentation updates (Limitations sections, before/after examples, JS runtime security hardening) are comprehensive and well-structured.
- Guard call symputx('jobstate',state) with %mf_existvar check to prevent
'Variable state is uninitialized' warning when the JSON root dataset
omits the state column (older API or unexpected response)
- Move %local jobstate, err_httpcode, err_msg declarations to the top of
the macro alongside existing %local statements (convention compliance)
- Gate the jobstate/loglocation debug %put behind mdebug=1 instead of
executing unconditionally on every call
- Add clarifying comment: when jobstate=failed but loglocation exists,
execution intentionally falls through to normal log fetching
- Add mf_existvar.sas to doxygen SAS Macros dependency list
There was a problem hiding this comment.
Code Review — 4gl-reviewer
Note: This PR mixes code with generated/docs/config files. Reviewing only the code change in viya/mv_getjoblog.sas. The all.sas change is the same macro in the generated concatenation file; .agents/skills/*/SKILL.md, sasjsconfig-schema.json, and skills-lock.json are docs/config — excluded.
Summary: Solid fix that surfaces real Viya job failure reasons (HTTP code + error message) instead of the opaque "URI is too short" message when a job fails before session creation. Logic is correct and well-structured.
Issues:
- [
viya/mv_getjoblog.sas:~196] — The outer%ifcondition includesjobstate=failed or jobstate=canceled, but the immediately nested%ifonly checksloglocation. When jobstate is failed/canceled and loglocation IS present, the outer block is entered but the inner block's condition is false, so nothing happens — thosejobstatechecks in the outer condition are effectively dead. The outer%ifcould be simplified to just%str(&loglocation)= or %str(&loglocation)=.since the inner guard already gates on that. - [
viya/mv_getjoblog.sas:~206] — If&libref1..errortable doesn't exist,err_httpcodeanderr_msgremain empty (declared%localbut never assigned), producing an awkward abort message:"Job failed, no log available. Error : ". Consider guarding theError &err_httpcode: &err_msgsuffix with a%length(&err_msg)>0check, or providing a fallback like"unknown".
Suggestions:
- [
viya/mv_getjoblog.sas:~186] — Consider also guarding the case where&libref1..roothas zero observations:call symputx('jobstate',state,'l')won't execute, leavingjobstateempty. The current code handles this (empty jobstate falls into the error path), but the abort message would say"Job , no log available..."with a dangling comma. A fallback%let jobstate=unknown;in the%elsebranch (whenstatevar doesn't exist) would make the message cleaner.
Looks good:
- Correct use of
%mf_existvarto defensively check for thestatecolumn before referencing it — handles Viya API response variations wherestatemay be absent. %sysfunc(exist(&libref1..error))guard before reading error details is good defensive practice.stop;in the error-reading data step prevents unnecessary I/O past the first row.err_httpcode/err_msg/jobstateproperly declared in%local— no scope leaks.- Debug
%putofjobstateandloglocationatmdebug=1aids troubleshooting. - New dependency on
mf_existvarcorrectly documented in the@liheader.
…ty error - Remove dead jobstate=failed/canceled checks from outer %if; the inner %if already gates on loglocation, so those checks were unreachable. Simplified to just check loglocation. - Guard the error suffix: when &libref1..error doesn't exist, err_httpcode/err_msg stay empty. Split into two mp_abort calls gated on %length(&err_msg) so we never emit 'Error : ' with empty values. - Add jobstate=unknown fallback when the state var is missing or root has zero observations, so the abort message reads cleanly instead of 'Job , no log available'. - Use mp_abort(iftrue=) pattern instead of bare mp_abort(mac=,msg=) per sasjs/core convention (avoids running inside %if/%else blocks). - Regenerate all.sas.
Replace the two mp_abort calls (one per err_msg branch) with a single mp_abort whose msg is built from abortmsg - set to the plain message by default and extended with error details only when err_msg is non-empty. Also declares abortmsg in the top-level %local and initialises err_httpcode/err_msg to empty so the fallback path is deterministic. Regenerate all.sas.
There was a problem hiding this comment.
Code Review — 4gl-reviewer
Summary: Good defensive fix that surfaces actual Viya job error details (HTTP code + message) when a job fails before session creation, instead of the opaque "URI is too short" message. The .agents/skills/ and skills-lock.json changes are docs/config — reviewing only the SAS code in viya/mv_getjoblog.sas.
Issues:
- [viya/mv_getjoblog.sas:196] — If
&libref1..errorexists but does not contain thehttpStatusCodeormessagecolumns (e.g. a different Viya API error schema version), thedata _null_step will error. Consider adding an%mf_existvarguard on those columns before the data step, similar to thestatevariable check above.
Suggestions:
- [viya/mv_getjoblog.sas:200] — The
stop;statement is good for short-circuiting after the first error row, but consider noting in a comment that the Viya error response may contain multiple rows and only the first is captured. - [viya/mv_getjoblog.sas:191] — The comment "If root had zero observations, jobstate was never set" is slightly misleading since the
%elsebranch sets it to'unknown'— but only if the data step has observations. Consider rewording to clarify that the%letfallback handles the zero-observations case specifically.
Looks good:
- Clean use of
%mf_existvarfor compile-time column detection — idiomatic SAS macro pattern. - Defensive fallback chain:
state→unknown→ error details → generic abort message. Each layer provides progressively more context. - New
%localdeclarations forjobstate,err_httpcode,err_msgprevent macro variable leakage. - The two
%mp_abortcalls (one with error details, one generic) ensure a useful message in all cases.
There was a problem hiding this comment.
Code Review — 4gl-reviewer
Summary: Good fix — when a Viya job fails before session creation (empty loglocation), the macro now surfaces the actual API error instead of the opaque "URI is too short" message. Logic is sound, defensive coding is thorough, and it follows existing codebase patterns. (Reviewing only the SAS code in viya/mv_getjoblog.sas + all.sas mirror; skill docs and skills-lock.json excluded.)
Looks good:
- Proper
%localdeclarations for all new variables (jobstate,err_httpcode,err_msg,abortmsg) %mf_existvar(&libref1..root,state)correctly handles the case where the JSON response lacks astatecolumn — resolved at compile time, conditionalcall symputxis the right pattern for optional JSON fields- Zero-observation fallback (
%if %str(&jobstate)= %then %let jobstate=unknown;) correctly handles emptyroottable where the data step body never executes %str(&loglocation)=.check catches numeric-missing-to-character conversion (.) — good defensive edge case handling- Abort message construction degrades gracefully: works with or without error details, and the
err_msglength check prevents an awkward trailing "Error :" when no error table exists all.sasmirror updated consistently with the source macro
Suggestions:
mv_getjoblog.sas:222—%mp_abort(iftrue=(1=1), ...)is redundant sinceiftruedefaults to%str(1=1); the existing unconditional abort at line 177 omits it. Harmless, but you could drop it for consistency.mv_getjoblog.sas:211-212— Theerrortable column nameshttpStatusCodeandmessageare assumed to exist without an%mf_existvarguard (unlikestateinroot). If the Viya error schema varies, this could produce a "Variable not found" ERROR. Low risk given the well-defined error response structure, but a%mf_existvarcheck would be consistent with the pattern used forstate.
…able When a job fails before a compute session is created (no loglocation), the abort message now includes the job URI so the full JSON response (including error details) can be fetched directly via a GET request to &base_uri&uri. Also gates the debug %put behind mdebug=1 and uses %mf_existvar to guard the jobstate read from the root dataset.
There was a problem hiding this comment.
Code Review — 4gl-reviewer
Summary: Good defensive enhancement — surfaces the actual error reason (HTTP code + message) when a Viya job fails before session creation, instead of the opaque "URI is too short" message. The code-only change is in viya/mv_getjoblog.sas; the all.sas change is the generated mirror, and the .agents/skills/ + skills-lock.json changes are docs/metadata — excluded from this code review.
Looks good:
- Consolidates new
%localvars (jobstate err_httpcode err_msg abortmsg) into the existing statement rather than adding duplicate lines — follows the multi-operand consolidation rule. %mf_existvarguard on thestatecolumn before referencing it is the right defensive pattern for JSON-parsed datasets where column presence varies.stop;after the first observation in the error-tabledata _null_step prevents unnecessary row reads — correct.- Fallback
%let jobstate=unknownwhen root has zero observations ensures the abort message always reads cleanly. - Checking both
%str(&loglocation)=and%str(&loglocation)=.covers both empty-string and numeric-missing cases from JSON parsing — thorough.
Suggestions:
- [viya/mv_getjoblog.sas:~L200] — The error-table
data _null_step referenceshttpStatusCodeandmessagewithout anmf_existvarguard (unlike thestatecheck above). If the Viya Jobs API ever returns an error table without these columns, thecall symputxwill ERROR. Consider wrapping in%if %mf_existvar(&libref1..error,httpStatusCode) and %mf_existvar(&libref1..error,message) %then %dofor consistency, or document that these columns are guaranteed by the API contract. - [viya/mv_getjoblog.sas:~L210] —
%mp_abort(iftrue=(1=1)...)is the idiomatic force-abort, but a brief comment explaining "1=1 forces unconditional abort" would help future maintainers who don't knowmp_abortsemantics.
Problem
When a Viya JES job fails before a compute session is created (e.g.
403onPOST /compute/contexts/.../sessions), the job response hasstate=failedandloglocationis missing (SAS missing value'.').The
mv_getjoblogmacro only checked for an emptyloglocation, not a missing value (.), so it fell through to the existing "validate log path" code — which reported the opaque and unhelpful error:This hid the actual failure reason from the user.
Fix
This patch:
jobstatefrom the JES response (in addition tologlocation)failed/canceledstate OR missing/emptyloglocation— including'.'(SAS missing value)errordataset from the JES response (httpStatusCode,message) when no log is availablemp_abortinstead of the opaque "URI is too short" messageBefore / After
Before:
After:
Test Plan
nextviya.emea.sas.com) — the 403 error is now surfaced correctly