pathom

dehli 2025-03-19T18:09:38.479639Z

Hey all! I'm just now checking out the latest version of pathom3. With the earlier version I was using, if I called (throw (ex-info "My custom error" {:error/code :my-error-code})) from a resolver, I could access the error data from the response by accessing ::p.error/error-data The latest version simplifies the error object and I'm only seeing :tx and :entity surfacing. When looking at com.wsscode.pathom3.error/datafy-processor-error the error I throw is available in ex-cause, but that's not returned from the function. I was wondering if there's something I'm missing to surface the thrown error data. Cheers!

2025-03-19T18:23:32.508749Z

This is probably more like feedback for pathom than something I would recommend anyone else do, but I have the following in a side project 🙈

(alter-var-root #'com.wsscode.pathom3.connect.runner/processor-exception (constantly (fn [_ ex] ex)))
(alter-var-root #'com.wsscode.pathom3.connect.runner/report-resolver-error (constantly (fn [_ _ ex] (throw ex))))

😄 1
dehli 2025-03-19T18:27:30.454039Z

Thanks for sharing! Never used alter-var-root, but that's very powerful.

wilkerlucio 2025-03-19T20:06:30.978799Z

hi @dehli, that was indeed an intended change to simplify the error, after the change you can get your ex-data as before in the following way:

(try
    (p.eql/process env {:users [{:user/id 1}]}
      [{:users [:user/full-name]}])
    (catch Throwable e
      (ex-data (ex-cause e))))

wilkerlucio 2025-03-19T20:07:01.551239Z

Pathom will wrap the error, but still use your original as the error cause, so you can grab it back with ex-cause

❗ 1
dehli 2025-03-19T20:14:41.103129Z

Thanks! I was using p.a.eql/boundary-interface so the errors would be automatically converted into maps. Would you recommend switching to regular process and constructing the map from ex-cause like you proposed?

wilkerlucio 2025-03-19T20:18:31.046449Z

that's a good question, the boundary api is designed to make something that can return data over the network, thus it does that kind of serialization. not using the boundary api is surely an option. another one is to make a plugin for ::pcr/wrap-resolver-error, where you can manipulate the error before it flows up

wilkerlucio 2025-03-19T20:18:50.730429Z

can you tell me more about your specific case? I think that is a part that is still up to change, if we find a better way to handle it

wilkerlucio 2025-03-19T20:20:44.230669Z

ps: please note that you might get an error there that is not about resolver exceptions, it could also be: • resolver missed returning some required attribute • resolver returned an invalid response (something that isn't a map or nil)

👍 1
dehli 2025-03-19T20:26:38.297109Z

Of course! I'm using boundary-interface right at the edge before sending the response to the consumer (so needs to be serializable). I currently throw custom exceptions from mutations when certain business rules aren't met. The consumer then reads those custom exceptions and reacts appropriately (I typically store all relevant data in the exception's data so client would read it from error-data with old pathom code). Hope that makes sense!

wilkerlucio 2025-03-20T14:03:05.888249Z

humm, gotcha, but that means the error you are bringing about is from a mutation? or its from some follow-up read from a mutation? Im trying to pin if the exception is coming from the mutation or a resolver

dehli 2025-03-20T14:41:52.533409Z

Ahh, sorry for not being clear. I use that pattern in both resolvers and mutations. In my codebase, mutations have more types of exceptions that can be thrown (spec errors, business logic, etc.) whereas resolvers will generally just throw (ex-info "Unauthorized" {:error/code :unauthorized}) if you try to access data that you don't have permissions for. In both cases I throw an ex-info with a unique :error/code.