Unhandled runtime error or uncaught (in promise) error

Unhandled runtime error or uncaught (in promise) error


3

I’m trying to make sure that Next.js doesn’t display the error, but rather I handle it on my code. I’m using Apollo-client to handle errors and I’m closely following the docs to make sure that I’m handling them properly. I’m currently getting a "Unhandled runtime error. Error: category already exists" displayed by Next.js, and on the Chrome browser console I’m getting a "Uncaught (in promise) Error: category already exists". However, I do notice that the error is showing up on my browser, but I just don’t want the Next.js error to show up.

Unhandled runtime error or uncaught (in promise) error

Unhandled runtime error or uncaught (in promise) error

Unhandled runtime error or uncaught (in promise) error

I’ve tried to reorganize my code so that the error state doesn’t have to go through too many children components. I’ve tried to change the type of Error from UserInputError to ApolloError if category exists. I’m not sure what i’m doing wrong. Please help.

Here’s my code:

This is my code to get data from the backend:

const addCategory = async ({ user_id, name }) => {
    await authorizeActivity({ user_id });

    const categoryExists = await getCategory({ name, user_id });

    if (categoryExists)
        throw new UserInputError('The category name already exists');

    const newCategory = await db
        .insert({ user_id, name }, '*')
        .into('category');

    if (!newCategory[0]) {
        throw new ApolloError('Something went wrong!');
    }

    return newCategory[0];
};

Here’s my page. Note that I pass my the error and loading states into the form component:

export default function NewCategory() {
    const router = useRouter();
    const user_id = Cookies.get('user');
   
    const {
        handleSubmit,
        register,
        formState: { errors, isDirty },
    } = useForm();

   
    const [addCategory, { data, loading, error }] = useMutation(ADD_CATEGORY);

 
    useEffect(() => {
        if (data) {
            router.push('/user/menu');
        }
    }, [data]);

    const onSubmit = (data) => {
        addCategory({
            variables: {
                user_id,
                name: data.category,
            },
        });
    };

    return (
        <Layout>
           
            <Form
                formState={isDirty}
                navigate={{
                    prev: {
                        message:
                            'Your changes will not be saved. Are you sure?',
                        link: '/user/menu',
                    },
                }}
                title={'Add new category'}
                onSubmit={handleSubmit(onSubmit)}
                button={
                    <button
                        type="submit"
                        style={{
                            padding: '16px 23px',
                            width: '100%',
                            maxWidth: '500px',
                            borderRadius: '8px',
                            marginTop: '10px',
                        }}
                    >
                        Add
                    </button>
                }
                state={{ loading, error }}
            >
                <Field
                    register={register('category', { required: 'Required' })}
                    info={{ name: 'category', label: 'Name' }}
                    errors={
                        errors.category
                            ? errors.category.message
                            : errors.category
                    }
                />
            </Form>
        </Layout>
    );
}

Here’s my form component:

export const Form = ({
    onSubmit,
    children,
    title,
    footer,
    state,
    button,
    className,
    navigate,
    formState,
}) => {
    const [warn, setWarn] = useState(false);
    const [warnMessage, setWarnMessage] = useState({ toggle: setWarn });
    const warning = ({ message, link, toggle }) => (
        <Alert
            pop
            dismissible={() => toggle(false)}
            state="warning"
            message={
                <>
                    {message}{' '}
                    <Link href={link} passHref>
                        <a
                            style={{
                                fontWeight: '400',
                                textDecoration: 'underline',
                            }}
                        >
                            Yes.
                        </a>
                    </Link>
                </>
            }
        />
    );

    return (
        <>
            {navigate ? (
                <div className={styles.navigate}>
                    {navigate.prev && (
                        <RiArrowLeftCircleLine
                            onClick={() => {
                                if (formState) {
                                    setWarnMessage({
                                        ...warnMessage,
                                        ...navigate.prev,
                                    });
                                    setWarn(!warn);
                                } else {
                                    router.push(navigate.prev.link);
                                }
                            }}
                        />
                    )}
                    {navigate.next && (
                        <RiArrowRightCircleLine onClick={navigate.next} />
                    )}
                </div>
            ) : null}

            {warn ? warning(warnMessage, setWarn) : null}

            <div className={styles.form_wrapper}>
                {(title && <div className={styles.form_title}>{title}</div>) ||
                    null}
                <form
                    onSubmit={onSubmit}
                    className={`${styles.form_inner} ${className || ''}`}
                >
                    {children}
                    {state &&
                    (state.loading || state.error || state.warning) ? (
                        <div className={styles.form_state}>
                            {state.loading ? (
                                <Loading />
                            ) : state.error ? (
                                <Alert
                                    state="error"
                                    message={
                                        state.error?.message ||
                                        'Something went wrong'
                                    }
                                />
                            ) : state.warning ? (
                                <Alert
                                    state="warning"
                                    message={state.warning.message}
                                />
                            ) : null}
                        </div>
                    ) : null}
                    {button || null}
                </form>
                {(footer && (
                    <div className={styles.form_footer}>{footer}</div>
                )) ||
                    null}
            </div>
        </>
    );
};

2 Answers
2


2

To fix the issue, i looked into the apollo docs more: https://www.apollographql.com/docs/react/data/error-handling/

I set an error policy: ‘all’, to render error information and any partial results.

  const [addCategory, { data, loading, error }] = useMutation(ADD_CATEGORY, {
        errorPolicy: 'all',
        onCompleted: (data) => {
            if (data.addCategory) {
                router.push('/user/menu');
            }
        },
    });

I was able to see the error on the client side, silencing errors on the console and browser, and any errors from Next.js.


0

If you want to avoid setting error-policy for each mutation or query hook then you can setup globally too:

 new ApolloFactory({
      ...
      defaultOptions: {
        query: {
          errorPolicy: "all",
        },
        mutate: {
          errorPolicy: "all",
        },
      },

})



Leave a Reply

Your email address will not be published. Required fields are marked *