fulcro

roklenarcic 2026-03-09T16:08:38.000859Z

Resolved Are mutations considered loads? I have added global-error-action to application and it never gets called when remote returns 500 response to a mutation. I’ve added a breakpoint in data-fetch namespace load-failed! function and it never gets called.

roklenarcic 2026-03-16T09:13:58.452349Z

You’ve got a bug here

roklenarcic 2026-03-16T09:14:10.037349Z

(= 403 status-code) (let [{:keys [security-id]} (ct/read (t/reader {}) body)]
                            (debounced-error
                              (str "Su petición está pidiendo acceso a recursos para los cuales no tiene acceso con REF " security-id ". Por favor contacta a soporte.")))

roklenarcic 2026-03-16T09:15:15.843189Z

so (t/reader {}) (I am assuming you’re using `

[cognitect.transit :as t]
will throw an error, the first argument should be a keyword like :json

tony.kay 2026-03-16T13:51:10.025849Z

Thanks

tony.kay 2026-03-15T16:33:35.715369Z

No, mutations are not considered loads. Other way around: loads are implemented as a mutation (which you can customize). The default-result-action! in mutations does support the global error action…Triggering it is done byt this code:

(>defn trigger-global-error-action!
  "When there is a `global-error-action` defined on the application, this function will checks for errors in the given
  mutation `env`. If any are found then it will call the global error action function with `env`.

  Typically used as part of the construction of a global default result handler for mutations.

  Always returns `env`."
  [env]
  [::env => ::env]
  (let [{:keys [app result]} env]
    (enc/when-let [global-error-action (ah/app-algorithm app :global-error-action)
                   remote-error?       (ah/app-algorithm app :remote-error?)
                   _                   (remote-error? result)]
      (global-error-action env))
    env))

👍 1
tony.kay 2026-03-15T16:35:37.770059Z

So you have to have a remote-error predicate defined (I think the default is not= status 200), and it has to return true for the result.

roklenarcic 2026-03-15T16:35:55.139399Z

Yeah I did manage to get it to work, but I had some issues getting errors back, seems like keys that weren’t in the query get removed

tony.kay 2026-03-15T16:36:12.361909Z

keys in the query?

roklenarcic 2026-03-15T16:37:14.341989Z

yes, if you return from the server map like {:error ""} then in the global error action you get {} , so I guess I need to add error keys to each query somehow for that to work

roklenarcic 2026-03-15T16:37:30.354539Z

ended up just returning a vector on errors, which gets ignored by this mechanism

tony.kay 2026-03-15T16:37:49.568269Z

possible for the tx to be rewritten…For example :ui/* keys. OH, that…your mutation has to declare what it expects as a return (mutation join) or your parser on the server might clip is

roklenarcic 2026-03-15T16:38:35.262909Z

No the frontend clipped it

roklenarcic 2026-03-15T16:38:53.097819Z

I saw the data in the http remote breakpoint

roklenarcic 2026-03-15T16:39:01.329209Z

I can try to find again the place where it gets clipped

tony.kay 2026-03-15T16:39:44.755879Z

Error handling is kind of tough, since you’re responsible for your own server design. The error handling story in Fulcro is intentionally “left to the app”. Hm…I would not expect Fulcro to be clipping it before that point. It’s getting the raw result from the remote and putting that in env and running it through the default-result-handler!…pretty sure, but not certain.

roklenarcic 2026-03-15T16:39:57.130329Z

com.fulcrologic.fulcro.algorithms.tx-processing/combine-sends line 124

roklenarcic 2026-03-15T16:40:52.992469Z

so yea, not possible to return “generic” errors (e.g. 401 session expired) as a response to transaction, keys get clipped here

roklenarcic 2026-03-15T16:41:11.892749Z

since you probably don’t have it as a mutation join

tony.kay 2026-03-15T16:41:12.346639Z

Ah

roklenarcic 2026-03-15T16:41:26.896369Z

ended up returning a vector for all my generic error needs

roklenarcic 2026-03-15T16:41:37.326319Z

from remote I mean

roklenarcic 2026-03-15T16:41:49.552829Z

then I get the data unmodified into the global error handler

tony.kay 2026-03-15T16:41:52.198569Z

Trying to remember why I did that…

tony.kay 2026-03-15T16:43:54.716689Z

I think it was to prevent spurious “extras” in the result from causing problems. It’s been a few years…

tony.kay 2026-03-15T16:47:26.765809Z

But think about it this way: • If a mutation has a problem, then catching the exception on the server in the mutation and returning a proper value would cause the body to contain {mutation-sym error-data-from-mutation}. This allows each mutation to handle it’s own errors individually, and is what you want. • If the server lets exceptions propagate all the way to the top, and you turn that into a status error, then you got NO proper errors from specific mutations. So that handling should be at a more global level. The “generic” status code is all that the mutation should be acting on. in this case. So, that’s why the result if filtered. If you have a server bug that’s putting garbage into the result that doesn’t correspond to the top level “things” (query keys or mutations) then they are considered garbage, and there’s some change them being there can screw up the post processing of the result. There’s no way for Fulcro to “target” the correct mutation(s) with results. That said, probably would be nice to just see the raw body/result somewhere in env if you wanted it.

roklenarcic 2026-03-15T16:50:14.186519Z

I mean there’s a whole class of errors that stop the app effectively (such as users losing session, 500 errors due to bugs, 403 errors for whatever reason) that I don’t really need to handle in a meaningful fashion but I do need to be able to send some text from server to the client to show the user the general nature of the error (also makes it easier to debug problems than making a generic error popup and then having to look at logs for every user with a problem), but I understand what you’re saying

tony.kay 2026-03-15T16:52:23.092659Z

What I do for those is handle them in the http remote middleware. I add a toast container or something, and trigger mutations or toats or whatever from the network layer.

tony.kay 2026-03-15T16:55:12.351939Z

e.g.

(defn- wrap-bad-status [handler]
  (fn [{:keys [status-code error body] :as resp}]
    (cond
      (= :network-error error) (debounced-error "Su solicitud falló porque se interrumpió la conexión de red o la solicitud estaba tardando demasiado.")
      (= 599 status-code) (prim/transact! @app-atom '[(dataico.ui.user.auth/system-outage)])
      (= 503 status-code) (debounced-error "Su solicitud falló porque el servidor no estaba disponible temporalmente.")
      (= 403 status-code) (let [{:keys [security-id]} (ct/read (t/reader {}) body)]
                            (debounced-error
                              (str "Su petición está pidiendo acceso a recursos para los cuales no tiene acceso con REF " security-id ". Por favor contacta a soporte.")))
      (= 490 status-code) (do
                            (debounced-message "Tu sesión ha caducado. Por favor autentícate de nuevo para continuar.")
                            (js/setTimeout #(scf/send! @app-atom :dataico.startup.model/startup :event/logout) 1000))
      (= 401 status-code) (prim/transact! @app-atom '[(dataico.ui.user.auth/request-not-authorized)])
      (not= 200 status-code) (debounced-error "Hubo un error inesperado. Inténtalo de nuevo."))
    (handler resp)))

tony.kay 2026-03-15T16:57:34.086489Z

So I don’t even consider any of those classes of errors to be “mutation errors” and I don’t really want them triggering any kind of other default error handling, nor do I bother to make an error-action section in all of my mutations.

tony.kay 2026-03-15T16:59:08.622039Z

Most mutations that “fail” are failing for some reason you can’t really handle…and if you can handle it, you should set up the mutation join and processing to actually cope with it (in which case I don’t consider them hard errors, but “ok” responses that have a different handling path).

sheluchin 2026-03-09T17:29:09.437309Z

Yes, they are. Afk so sorry for lack of citation but it's in there somewhere.

roklenarcic 2026-03-09T17:30:09.150759Z

Hm ok I will triple check but my load-failed function doesn't get called