wp-graphql-upload icon indicating copy to clipboard operation
wp-graphql-upload copied to clipboard

[ApolloError: Internal server error]

Open DevelopMod opened this issue 1 year ago • 11 comments

I have made it like this, this is not an error but I also do not understand, if the issue is my setup or the problem with wp-graphql-upload.

Can you please review it? thanks

		register_graphql_mutation('uploadProfilePicture', [
			'inputFields' => [
				'file' => [
					'type' => 'Upload',
				],
			],
			'outputFields' => [
				'avatarUrl' => [
					'type' => 'String',
					'resolve' => function ($payload) {
						return $payload['avatarUrl'];
					}
				],
				'avatarUrlThumbnail' => [
					'type' => 'String',
					'resolve' => function ($payload) {
						return $payload['avatarUrlThumbnail'];
					}
				],
				'successMessage' => [
					'type' => 'Boolean',
					'resolve' => function ($payload) {
						return $payload['successMessage'];
					}
				]
			],
			'mutateAndGetPayload' => function ($input, $context, $info) {
				if (!function_exists('wp_handle_sideload')) {
					require_once(ABSPATH . 'wp-admin/includes/file.php');
				}

				$file_return = wp_handle_sideload($input['file'], [
					'test_form' => false,
					'test_type' => false,
				]);

				if (isset($file_return['error']) || isset($file_return['upload_error_handler'])) {
					throw new \GraphQL\Error\UserError("The file could not be uploaded.");
				}
				
				$filename = $file_return['file'];

				$attachment = [
					'guid' => $file_return['url'], 
					'post_mime_type' => $file_return['type'],
					'post_title' => preg_replace('/\.[^.]+$/', '', basename($filename)),
					'post_content' => '',
					'post_status' => 'inherit'
				];

				$attachment_id = wp_insert_attachment($attachment, $filename);

				require_once(ABSPATH . 'wp-admin/includes/image.php');
				$attach_data = wp_generate_attachment_metadata($attachment_id, $filename);
				wp_update_attachment_metadata($attachment_id, $attach_data);

				update_field('profile_pic', $attachment_id, 'user_' . $current_user->ID);

				$profile_pic = get_field('profile_pic', 'user_' . $current_user->ID);
				$avatarUrl = $profile_pic['url'];
				$avatarUrlThumbnail = $profile_pic['sizes']['thumbnail'];

				return [
					'avatarUrl' => $avatarUrl,
					'avatarUrlThumbnail' => $avatarUrlThumbnail,
					'successMessage' => true,
				];
			}
		]);

Uploading it this way:

	const UPLOAD_IMAGE_MUTATION = gql`
                    mutation UploadProfilePicture($input: UploadProfilePictureInput!) {
                      uploadProfilePicture(input: $input) {
                        avatarUrl
                        avatarUrlThumbnail
                      }
                    }
                  `;
                
                const selectImage = async () => {
             
			// Check for the permission
              const permissionResult =
                await ImagePicker.requestMediaLibraryPermissionsAsync();
          
              if (permissionResult.granted === false) {
                alert("You've refused to allow this app to access your photos!");
                return;
              }
          
              // Open the image picker
              let pickerResult = await ImagePicker.launchImageLibraryAsync({
                mediaTypes: ImagePicker.MediaTypeOptions.Images,
                allowsEditing: true, // or false based on your requirement
                aspect: [4, 3], // aspect ratio
                quality: 1, // quality of the image
              });
          
              if (pickerResult.canceled === true) {
                return;
              }
          
              // Access the selected asset
              const selectedAsset = pickerResult.assets[0]; // Assuming single image selection
          
              if (selectedAsset) {
                const source = {
                  uri: selectedAsset.uri,
                  type: selectedAsset.type, // type is now part of the asset
                  name: selectedAsset.fileName || "profile-pic.jpg", // name can be accessed or set a default
                };
          
                console.log(source);
                uploadImage(source);
              } else {
                console.error("No image selected");
              }
            };
          
            const [uploadProfilePicture] = useMutation(UPLOAD_IMAGE_MUTATION);
          
            const uploadImage = async (imageFile) => {
              // Convert image file to a format suitable for GraphQL upload
              let localUri = imageFile.uri;
              let filename = localUri.split("/").pop();
          
              // Infer the type of the image
              let match = /\.(\w+)$/.exec(filename);
              let type = match ? `image/${match[1]}` : imageFile.type;
          
              // Prepare the formData
              const formData = new FormData();
              formData.append("file", { uri: localUri, name: filename, type });
          
              console.log("Form Data Prepared:", formData);
          
              try {
                console.log("Sending request to server");
                const response = await uploadProfilePicture({
                  variables: {
                    input: { file: imageFile }, // Modify here to match the GraphQL mutation
                  },
                });
          
                console.log("Response received:", response);
                // Extract the data from the response
                const { avatarUrl, avatarUrlThumbnail, successMessage } =
                  response.data.uploadProfilePicture;
                if (successMessage) {
                  console.log(successMessage); // Log or handle the success message as needed
                  // Update user context with new image URLs
                  setUserData({ avatarUrl, avatarUrlThumbnail });
                }
              } catch (error) {
                console.error("Error during image upload:", error);
                console.error("Error details:", error.networkError?.result || error);
              }
            };

The problem is am still getting this error: ERROR Error during image upload: [ApolloError: Internal server error] ERROR Error details: [ApolloError: Internal server error]

DevelopMod avatar Nov 15 '23 08:11 DevelopMod

I'd start with formData.append("file", { uri: localUri, name: filename, type });, since you need to feed it an actual File or Blob object.

Then fix your catch statement at the end (or add an error handler to your ApolloClient definition), so you can get the actual error data and not just the generic message and continue your debugging.

justlevine avatar Nov 15 '23 09:11 justlevine

I do have an errorLink:


const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors)
    graphQLErrors.forEach(({ message, locations, path }) =>
      console.error(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
      )
    );

  if (networkError) console.error(`[Network error]: ${networkError}`);
});

The thing is, am not getting the root of the error... Even tho, according to here, this should work in expo: https://stackoverflow.com/questions/66372873/react-native-expo-post-blob#comment117341523_66372873


const uploadImage = async (imageFile) => {
    try {
      // Extract the file extension
      let fileType = imageFile.uri.substring(
        imageFile.uri.lastIndexOf(".") + 1
      );

      // Prepare the formData
      const formData = new FormData();
      formData.append("file", {
        uri: imageFile.uri,
        name: imageFile.name, // You can use a dynamic name based on the context
        type: `image/${fileType}`,
      });

      console.log("Form Data Prepared:", formData);

      console.log("Sending request to server");
      const response = await uploadProfilePicture({
        variables: {
          input: { file: formData }, // Modify here to match the GraphQL mutation
        },
      });

      console.log("Response received:", response);
      // Extract the data from the response
      const { avatarUrl, avatarUrlThumbnail, successMessage } =
        response.data.uploadProfilePicture;
      if (successMessage) {
        console.log(successMessage); // Log or handle the success message as needed
        // Update user context with new image URLs
        setUserData({ avatarUrl, avatarUrlThumbnail });
      }
    } catch (error) {
      console.error("Error during image upload:", error);
      if (
        error.networkError &&
        error.networkError.result &&
        error.networkError.result.errors
      ) {
        error.networkError.result.errors.forEach((e) =>
          console.error(e.message)
        );
      }
    }
  };

But I'm still getting: LOG {"name": "profile-pic.jpg", "type": "image", "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FCudl-6de743e2-8e52-4646-b93f-9ed9ba6b740c/ImagePicker/60ced4f4-58f9-443c-ba72-27ad763aae27.jpeg"} LOG Form Data Prepared: {"_parts": [["file", [Object]]]} LOG Sending request to server ERROR [GraphQL error]: Message: Internal server error, Location: [object Object], Path: undefined ERROR Error during image upload: [ApolloError: Internal server error]

DevelopMod avatar Nov 15 '23 10:11 DevelopMod

Since it's an Internal server error, try enabling WP_DEBUG and GRAPHQL_DEBUG to see more info and check the graphql response in your browser devtools since it might be a malformed response.

Add this to your wp-config.php:

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('SCRIPT_DEBUG', true);
define('GRAPHQL_DEBUG', true);

dre1080 avatar Nov 15 '23 15:11 dre1080

Since it's an Internal server error, try enabling WP_DEBUG and GRAPHQL_DEBUG to see more info and check the graphql response in your browser devtools since it might be a malformed response.

Add this to your wp-config.php:

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('SCRIPT_DEBUG', true);
define('GRAPHQL_DEBUG', true);

Thanks for your reply @dre1080 , sadly I have all of them enabled :


define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', true);
@ini_set('display_errors',1 );
define('SCRIPT_DEBUG', true);
define('GRAPHQL_DEBUG', true);

Also, just to make sure, I get no error I have added your sample upload mutation in the readme and tried to upload the image to it:


const SIMPLE_UPLOAD_MUTATION = gql`
  mutation UploadFile($input: UploadInput!) {
    upload(input: $input) {
      text
    }
  }
`;

const selectImage = async () => {
    // Check for the permission
    const permissionResult =
      await ImagePicker.requestMediaLibraryPermissionsAsync();

    if (permissionResult.granted === false) {
      alert("You've refused to allow this app to access your photos!");
      return;
    }

    // Open the image picker
    let pickerResult = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      allowsEditing: true, // or false based on your requirement
      aspect: [4, 4], // aspect ratio
      quality: 1, // quality of the image
    });

    if (pickerResult.canceled === true) {
      return;
    }

    // Access the selected asset
    const selectedAsset = pickerResult.assets[0]; // Assuming single image selection

    if (selectedAsset) {
      const source = {
        uri: selectedAsset.uri,
        type: selectedAsset.type, // type is now part of the asset
        name: selectedAsset.fileName || "profile-pic.jpg", // name can be accessed or set a default
      };

      console.log(source);
      uploadImage(source);
    } else {
      console.error("No image selected");
    }
  };

const [uploadFileMutation] = useMutation(SIMPLE_UPLOAD_MUTATION);

const uploadImage = async (imageFile) => {
    const blob = await new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.onload = function () {
        resolve(xhr.response);
      };
      xhr.onerror = function (e) {
        console.log(e);
        reject(new TypeError("Network request failed"));
      };
      xhr.responseType = "blob";
      xhr.open("GET", imageFile.uri, true);
      xhr.send(null);
    });

    try {
      const response = await uploadFileMutation({
        variables: {
          input: { file: blob },
        },
      });

      console.log("File uploaded successfully:", response.data.upload.text);
    } catch (error) {
      console.error("Error uploading file:", error);
    }
  };


All I get back is: LOG {"name": "profile-pic.jpg", "type": "image", "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FCudl-6de743e2-8e52-4646-b93f-9ed9ba6b740c/ImagePicker/c5930b63-c616-4b68-a965-657972e121cb.jpeg"} ERROR [GraphQL error]: Message: Internal server error, Location: [object Object], Path: undefined ERROR Error uploading file: [ApolloError: Internal server error]

DevelopMod avatar Nov 15 '23 15:11 DevelopMod

Finally, I have got this error:


 LOG  {"name": "profile-pic.jpg", "type": "image", "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FCudl-6de743e2-8e52-4646-b93f-9ed9ba6b740c/ImagePicker/766861b2-99b5-4ad8-b588-9144a4eeed8a.jpeg"}
 LOG  {"_data":{"lastModified":0,"name":"766861b2-99b5-4ad8-b588-9144a4eeed8a.jpeg","size":722647,"offset":0,"type":"image/jpeg","blobId":"54ac845e-f2cf-4fa4-93b8-ad5f7a978a97","__collector":{}}}
 LOG  Sending request to server
 ERROR  [GraphQL error]: Message: Variable "$input" got invalid value {"_parts":[["file",{"_data":{"lastModified":0,"name":"766861b2-99b5-4ad8-b588-9144a4eeed8a.jpeg","size":722647,"offset":0,"type":"image\/jpeg","blobId":"54ac845e-f2cf-4fa4-93b8-ad5f7a978a97","__collector":[]}}]]}; Field "_parts" is not defined by type UploadProfilePictureInput., Location: [object Object], Path: undefined

DevelopMod avatar Nov 15 '23 16:11 DevelopMod

Did you try this yet? Maybe there's something off about whatever automagical transformation is happening.

I'd start with formData.append("file", { uri: localUri, name: filename, type });, since you need to feed it an actual File or Blob object.

justlevine avatar Nov 15 '23 16:11 justlevine

Did you try this yet? Maybe there's something about whatever automagical transformation is happening.

I'd start with formData.append("file", { uri: localUri, name: filename, type });, since you need to feed it an actual File or Blob object.

yup, already tried to be honest. Not working .. Getting the same error, or similar errors invalid input.

DevelopMod avatar Nov 15 '23 16:11 DevelopMod

Okay, the error disappeared or changed when tried this one:

const uploadImage = async (imageFile) => {
    try {
      const blob = await new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.onload = function () {
          resolve(xhr.response);
        };
        xhr.onerror = function (e) {
          console.log(e);
          reject(new TypeError("Network request failed"));
        };
        xhr.responseType = "blob";
        xhr.open("GET", imageFile.uri, true);
        xhr.send(null);
      });

      console.log(JSON.stringify(blob));

      const formData = new FormData();
      //formData.append("file", blob, imageFile.name);
      formData.append("userpic", blob, "chris1.jpg");

      // const test = {
      //   file: blob
      // };

      console.log("Sending request to server");
      const graphqlResponse = await uploadProfilePicture({
        variables: {
          input: { file: formData._parts[0].file }, // Modify here to match the GraphQL mutation
        },
      });

      console.log("Response received:", graphqlResponse);
      // Extract the data from the response
      const { avatarUrl, avatarUrlThumbnail, successMessage } =
        response.data.uploadProfilePicture;
      if (successMessage) {
        console.log(successMessage); // Log or handle the success message as needed
        // Update user context with new image URLs
        setUserData({ avatarUrl, avatarUrlThumbnail });
      }
    } catch (error) {
      console.error(
        "Error during image upload:",
        error.graphQLErrors[0].debugMessage
      );
      if (
        error.networkError &&
        error.networkError.result &&
        error.networkError.result.errors
      ) {
        error.networkError.result.errors.forEach((e) =>
          console.error(e.message)
        );
      }
    }
  };

 LOG  {"name": "profile-pic.jpg", "type": "image", "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FCudl-6de743e2-8e52-4646-b93f-9ed9ba6b740c/ImagePicker/ad852a20-7def-4d9c-a481-0718304cfe09.jpeg"}
 LOG  {"_data":{"lastModified":0,"name":"ad852a20-7def-4d9c-a481-0718304cfe09.jpeg","size":722647,"offset":0,"type":"image/jpeg","blobId":"d37a64c4-c314-4a49-9e56-2eaa54926960","__collector":{}}}
 LOG  Sending request to server
 ERROR  [GraphQL error]: Message: The file could not be uploaded., Location: [object Object], Path: uploadProfilePicture       
 ERROR  Error during image upload: undefined

DevelopMod avatar Nov 15 '23 16:11 DevelopMod

so it only accepted my input when I tried the blob._data but still failed on server side :(


const uploadImage = async (imageFile) => {
    try {
      const blob = await new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.onload = function () {
          resolve(xhr.response);
        };
        xhr.onerror = function (e) {
          console.log(e);
          reject(new TypeError("Network request failed"));
        };
        xhr.responseType = "blob";
        xhr.open("GET", imageFile.uri, true);
        xhr.send(null);
      });

      const formData = new FormData();
      formData.append("userpic", blob, "chris1.jpg");

      console.log(blob._data);

      const graphqlResponse = await uploadProfilePicture({
        variables: {
          input: { file: blob._data }, // Modify here to match the GraphQL mutation
        },
      });

      // const graphqlResponse = await uploadFileMutation({
      //   variables: {
      //     input: { file: formData._parts[0][1]._data },
      //   },
      // });

      console.log("Response received:", graphqlResponse);
      const { avatarUrl, avatarUrlThumbnail, successMessage } =
        response.data.uploadProfilePicture;
      if (successMessage) {
        console.log(successMessage);
        setUserData({ avatarUrl, avatarUrlThumbnail });
      }
    } catch (error) {
      console.error(
        "Error during image upload:",
        error.graphQLErrors[0].debugMessage
      );
      console.error("Error during image upload:", error);
    }
  };

Here the error log :


[15-Nov-2023 17:26:28 UTC] Received file: Array
(
    [lastModified] => 0
    [name] => 13436d64-b9c0-46a1-a375-e0b049b396c0.jpeg
    [size] => 722647
    [offset] => 0
    [type] => image/jpeg
    [blobId] => a5ead176-e0c4-4714-a700-fb475ebf7fcc
    [__collector] => Array
        (
        )

    [tmp_name] => /tmp/13436d64-b9c0-46a1-a375-e0b049b396c0.jpeg
)

[15-Nov-2023 17:26:28 UTC] uploadProfilePicture mutation called
[15-Nov-2023 17:26:28 UTC] Handling file upload
[15-Nov-2023 17:26:28 UTC] File upload error: {"error":"Specified file failed upload test."}

DevelopMod avatar Nov 15 '23 17:11 DevelopMod

That error comes from WP's handle_file_upload().

I've run into something similar myself in the past over in #2 , was able to get around it by using wp_upload_bits() on the server side, since that doesn't rely on the is_file_uploaded() check that seems to be failing.

justlevine avatar Nov 15 '23 18:11 justlevine

After using wp_upload_bits it most likely fails to upload the image or the photo.. and save a photo with 0 byte in the uplaod dir:

Code:


register_graphql_mutation('uploadProfilePicture', [
			'inputFields' => [
				'file' => [
					'type' => 'Upload',
				],
			],
			'outputFields' => [
				'avatarUrl' => [
					'type' => 'String',
					'resolve' => function ($payload) {
						return $payload['avatarUrl'];
					}
				],
				'avatarUrlThumbnail' => [
					'type' => 'String',
					'resolve' => function ($payload) {
						return $payload['avatarUrlThumbnail'];
					}
				],
				'successMessage' => [
					'type' => 'Boolean',
					'resolve' => function ($payload) {
						return $payload['successMessage'];
					}
				]
			],
			'mutateAndGetPayload' => function ($input, $context, $info) {		

				$upload = wp_upload_bits($input['file']['name'], null, file_get_contents($input['file']['tmp_name']));
				
				if (!$upload['error']) {
					error_log('Upload: ' . print_r($upload, true));
					$filename = $upload['file'];
					
					$attachment = [
						'guid' => $file_return['url'], 
						'post_mime_type' => $file_return['type'],
						'post_title' => preg_replace('/\.[^.]+$/', '', basename($filename)),
						'post_content' => '',
						'post_status' => 'inherit'
					];

					$attachment_id = wp_insert_attachment($attachment, $filename);

					require_once(ABSPATH . 'wp-admin/includes/image.php');
					$attach_data = wp_generate_attachment_metadata($attachment_id, $filename);
					wp_update_attachment_metadata($attachment_id, $attach_data);

					update_field('profile_pic', $attachment_id, 'user_' . $current_user->ID);

					$profile_pic = get_field('profile_pic', 'user_' . $current_user->ID);
					$avatarUrl = $profile_pic['url'];
					$avatarUrlThumbnail = $profile_pic['sizes']['thumbnail'];

					return [
						'avatarUrl' => $avatarUrl,
						'avatarUrlThumbnail' => $avatarUrlThumbnail,
						'successMessage' => true,
					];
				} else {
					error_log('File upload error: ' . $upload['error']);
					throw new \GraphQL\Error\UserError("The file could not be uploaded.");
				}
				
			}
		]);

Log:


[15-Nov-2023 18:25:33 UTC] PHP Warning:  file_get_contents(/tmp/photo.jpeg): Failed to open stream: No such file or directory in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 404
[15-Nov-2023 18:25:33 UTC] Upload: Array
(
    [file] => /home/**PATH**/wp-content/uploads/2023/11/photo-2.jpeg
    [url] => https://testDomain/wp-content/uploads/2023/11/photo-2.jpeg
    [type] => image/jpeg
    [error] => 
)

[15-Nov-2023 18:25:33 UTC] PHP Warning:  Undefined variable $file_return in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 411
[15-Nov-2023 18:25:33 UTC] PHP Warning:  Trying to access array offset on value of type null in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 411
[15-Nov-2023 18:25:33 UTC] PHP Warning:  Undefined variable $file_return in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 412
[15-Nov-2023 18:25:33 UTC] PHP Warning:  Trying to access array offset on value of type null in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 412
[15-Nov-2023 18:25:33 UTC] PHP Deprecated:  preg_replace(): Passing null to parameter #3 ($subject) of type array|string is deprecated in /home/**PATH**/wp-includes/formatting.php on line 5725
[15-Nov-2023 18:25:33 UTC] PHP Warning:  Undefined variable $current_user in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 424
[15-Nov-2023 18:25:33 UTC] PHP Warning:  Attempt to read property "ID" on null in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 424
[15-Nov-2023 18:25:33 UTC] PHP Warning:  Undefined variable $current_user in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 426
[15-Nov-2023 18:25:33 UTC] PHP Warning:  Attempt to read property "ID" on null in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 426
[15-Nov-2023 18:25:33 UTC] PHP Warning:  Undefined array key "sizes" in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 428
[15-Nov-2023 18:25:33 UTC] PHP Warning:  Trying to access array offset on value of type null in /home/**PATH**/wp-content/themes/headless-xfive/functions.php on line 428

DevelopMod avatar Nov 15 '23 18:11 DevelopMod