Hot questions for Handling IllegalArgumentException in Retrofit

Top 10 Java Open Source / Retrofit / Handling IllegalArgumentException

Retrofit throwing IllegalArgumentException exception for asynchronous FormUrlEncoded DELETE call

Question: I'm trying to make an asynchronous POST and DELETE which is form url encoded using Retrofit in Android 4.4

Here is my client

@FormUrlEncoded
@POST(INetwork.API_BASE_PREFIX + "/memberships.json")
void join(@Field("id") String id, Callback<?> cb);

@FormUrlEncoded
@DELETE(INetwork.API_BASE_PREFIX + "/memberships.json")
void leave(@Field("id") String id, Callback<?> cb);

And this is the exception

java.lang.IllegalArgumentException: IRepositoryClient.leave: FormUrlEncoded can only be specified on HTTP methods with request body (e.g., @POST).
        at retrofit.RestMethodInfo.methodError(RestMethodInfo.java:118)
        at retrofit.RestMethodInfo.parseMethodAnnotations(RestMethodInfo.java:191)
        at retrofit.RestMethodInfo.init(RestMethodInfo.java:128)
        at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:329)
...

I looked through the source and it basically if the method doesn't have a body and the request is formurlencoded, this exception is thrown. Also I noticed that all example of FormUrlEncoded work fine when it is not asynchronous, i.e. I have a return type of some sort and no callback - sorry I'm a little lost

Should I send an empty body? Am I required to send one and won't the @Field parameters suffice?

Answer: The RFC for HTTP is unclear on whether or not the DELETE method is allowed to have a request body or not. Retrofit is raising an error on the side of caution by not having one.

However, you can still include one (assuming the HTTP client supports it) by using a custom HTTP method annotation.

package com.myapp;

@Target(METHOD)
@Retention(RUNTIME)
@RestMethod(value = "DELETE", hasBody = true)
public @interface BODY_DELETE {
  String value();
}

Now specify your interface method using the custom annotation that you defined.

@FormUrlEncoded
@BODY_DELETE(INetwork.API_BASE_PREFIX + "/memberships.json")
void leave(@Field("id") String id, Callback<?> cb);

Updated answer for Retrofit 2.0:

Retrofit 2 doesn't seem to have @RestMethod any more, so here is what works:

@FormUrlEncoded
@HTTP(method = "DELETE", path = INetwork.API_BASE_PREFIX + "/memberships.json", hasBody = true)
void leave(@Field("id") String id, Callback<?> cb);

For retrofit 2.+

@FormUrlEncoded
@HTTP(method = "DELETE", path = INetwork.API_BASE_PREFIX + "/memberships.json", hasBody = true)
Callback<?> cb(@Field("id") String id);

and for RxRetrofit 2.+

@FormUrlEncoded
@HTTP(method = "DELETE", path = INetwork.API_BASE_PREFIX + "/memberships.json", hasBody = true)
Observable<?> cb(@Field("id") String id);

IllegalArgumentException in Retrofit / must not have replace block

Question: I have the following code :

@GET("api.php?company_name={name}")
Call<Model> getRoms_center(@Query("name") String name);

According to the official docs, I must use @Query, and i'm using it, but i'm getting the following error :

java.lang.IllegalArgumentException: URL query string "company_name={name}" must not have replace block. For dynamic query parameters use @Query.

Answer: You should do it like that instead:

@GET("api.php")
Call<Model> getRoms_center(@Query("company_name") String name);

Using @FieldMap and @Part in single request in Retrofit2 gets java.lang.IllegalArgumentException: Only one encoding annotation is allowed.for method

Question: This might seem similar to earlier questions but none actually answers my question. I need to Post multiple fields and multiple images in one request using retrofit2 and i'm getting this error

java.lang.IllegalArgumentException: Only one encoding annotation is allowed.for method xxx

i'm using

@Multipart
@FormUrlEncoded

since @Field requires @FormUrlEncoded and @Part requires @Multipart. the more logical thing to do is to remove the @FormUrlEncoded annotation, but how do i go from there. Now the question is how do i go about the task to achieve sending my post in a single request.

here's the interface

@Multipart
@FormUrlEncoded
@POST("upload")
Call<ResponseBody> uploadPost(@FieldMap Map<String, String> map,
                       @Part MultipartBody.Part image1,
                       @Part MultipartBody.Part image2,
                       @Part MultipartBody.Part image3);

Answer: use this interface.

@Multipart
@POST("upload")
Call<ResponseBody> uploadPost(
        @PartMap() Map<String, RequestBody> descriptions,
        @Part List<MultipartBody.Part> images);

Retrofit 2.0 throwing “IllegalArgumentException: @Field parameters can only be used with form encoding”. How to do right API query and fix it?

Question: My problem is that I don't know how to start using Retrofit 2.0 with received API - mentioned below.

Firstly, I need to username, password, fbID (optional), gmailID (optional), twitID (optional), gender, birthDate, location (not required - if long and lat has values), longitude (optional), latitude (optional), profileImage (optional).

When all parameters are good - receive status = true. If not - receive status = false and required parameters which are wrong (e.g. mail is already taken)

So I can receive status = true or status = false and array with max 5 parameters (username, email, password, gender, birthDate).

I tried this API Interface:

public interface AuthRegisterUserApi {
    @PUT()
    Call<AuthRegisterUserModel> getStatus(
            @Field("username") String username,
            @Field("email") String email,
            @Field("password") String password,
            @Field("fbID") String fbID,
            @Field("gmailID") String gmailID,
            @Field("twitID") String twitID,
            @Field("gender") String gender,
            @Field("birthDate") String birthDate,
            @Field("location") String location,
            @Field("longitude") String longitude,
            @Field("latitude") String latitude,
            @Field("profileImage") String profileImage
            );

    class Factory {
        private static AuthRegisterUserApi service;

        public static AuthRegisterUserApi getInstance() {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(ApiConstants.REGISTER_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

            service = retrofit.create(AuthRegisterUserApi.class);

            return service;
        }
    }
}

and this code in Activity:

AuthRegisterUserApi.Factory.getInstance()
        .getStatus(
                usernameEditText.getText().toString(),
                emailEditText.getText().toString(),
                passwordEditText.getText().toString(),
                "", "", "",
                (maleRadioButton.isChecked() ? "male" : "female"),
                mYear + "-" + mMonth+1 + "-" + mDay,
                (geoLocationToggleButton.isChecked() ? geoLocationEditText.getText().toString() : ""),
                (!geoLocationToggleButton.isChecked() ? "50" : ""),
                (!geoLocationToggleButton.isChecked() ? "50" : ""),
                "")
        .enqueue(new Callback<AuthRegisterUserModel>() {
    @Override
    public void onResponse(Call<AuthRegisterUserModel> call, Response<AuthRegisterUserModel> response) {
        if(response.isSuccessful()) {
            if (response.body().isStatus()) {
                showToast(getApplicationContext(), "Registration ok.");
            } else {
                response.body().getInfo().getUsername();
            }
        }
    }

    @Override
    public void onFailure(Call<AuthRegisterUserModel> call, Throwable t) {

    }
});

I have error: “java.lang.IllegalArgumentException: @Field parameters can only be used with form encoding. (parameter #1) for method AuthRegisterUserApi.getStatus”

I tried to register user using Postman and it works when I used option Body -> x-www-form-urlencoded.

Answer: Your request is not encoded right, but are postman, so do change that :

public interface AuthRegisterUserApi {
    @FormUrlEncoded
    @PUT("/api/register")
    Call<AuthRegisterUserModel> getStatus(
            @Field("username") String username,
            @Field("email") String email,
            @Field("password") String password,
            @Field("fbID") String fbID,
            @Field("gmailID") String gmailID,
            @Field("twitID") String twitID,
            @Field("gender") String gender,
            @Field("birthDate") String birthDate,
            @Field("location") String location,
            @Field("longitude") String longitude,
            @Field("latitude") String latitude,
            @Field("profileImage") String profileImage
            );

    class Factory {
        private static AuthRegisterUserApi service;

        public static AuthRegisterUserApi getInstance() {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(ApiConstants.BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

            service = retrofit.create(AuthRegisterUserApi.class);

            return service;
        }
    }
}

java.lang.IllegalArgumentException: API interfaces must not extend other interfaces Retrofit 2

Question: I am having the next problem using Retrofit 2 beta 2:

java.lang.IllegalArgumentException: API interfaces must not extend other interfaces.

This is because I have one interface for the API of Retrofit like this:

public interface RetrofitBaseAPI {

    @POST
    Call<LoginResp> login(@Url String url, @Body LoginReq loginReq);

    @POST
    Call<String> logout(@Url String url, @Header("Cookie") String sid);
}

For example, one of them is this one:

public interface RetrofitWiserLinkAPI extends RetrofitBaseAPI {

    @GET("/rs/DeviceIdentification")
    Call<DeviceId> getDeviceIdentification(@Header("Cookie") String sid);
}

And then, I have three other interfaces, the three of them extends from this RetrofitBaseAPI interface.

When I try to call the retrofit.create(Class class) with the given interface, I always receive this error.

As far as I was reading, the only solution is to create three independents interfaces. Is it true? Anybody knows another solution?

Answer: It's not possible to have a base Retrofit interface.

Retrofit favors composition over inheritance. One interface per service.

So as you already found out, the only solution is to create three independents interfaces.


Retrofit-IllegalArgumentException: unexpected url

Question: I want to upload a video file (chosen from gallery) to a server using Retrofit. But it doesn't work and throws the exception "java.lang.IllegalArgumentException: unexpected url: 192.168.1.7". My code is presented below.

PostFile.java:

public final class PostFile {
    public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("vide/mp4");

    private final OkHttpClient client = new OkHttpClient();

    public void run(String path) throws Exception {
        File file = new File(path);

        Request request = new Request.Builder()
            .url("192.168.1.7/")
            .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
            .build();

        Response response = client.newCall(request).execute();
        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

        System.out.println(response.body().string());
    }
}

PostFile:

public class MainActivity extends Activity {
    private static int RESULT_LOAD_IMG = 1;
    String decodableString;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void loadImagefromGallery(View view) {
        // Create intent to Open Image applications like Gallery, Google Photos
        Intent galleryIntent = new Intent(Intent.ACTION_PICK,
                android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
        // Start the Intent
        startActivityForResult(galleryIntent, RESULT_LOAD_IMG);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        try {
            // When an Image is picked
            if (requestCode == RESULT_LOAD_IMG && resultCode == RESULT_OK
                    && null != data) {
                // Get the Image from data

                Uri selectedVideo = data.getData();

                String[] filePathColumn = { MediaStore.Video.Media.DATA };
                // Get the cursor
                Cursor cursor = getContentResolver().query(selectedVideo,
                        filePathColumn, null, null, null);
                // Move to first row
                cursor.moveToFirst();

                int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
                decodableString = cursor.getString(columnIndex);
                cursor.close();
                new PostFile().run(decodableString);
                Log.i("mohsen","done");
            } else {
                Toast.makeText(this, "You haven't picked any video",
                        Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(this, "Something went wrong", Toast.LENGTH_LONG)
                    .show();
        }
    }
}

Wampserver is running Apache Server 2.4.4 on my computer. Please note that I have no idea whether this code is sound or not, and I'm just trying to make it work almost blindly.

Answer: Try change

.url("192.168.1.7/")

to

.url("http://192.168.1.7")

Retrofit - throwing an exception java.lang.IllegalArgumentException: Only one encoding annotation is allowed

Question: Hello guys here is my sample code

@FormUrlEncoded
@Multipart
@POST("registration.php")
Call<Signup> getSignupResponse(@Field("email") String email,
                               @Field("lname") String lname,
                               @Field("fname") String fname,
                               @Field("password") String password,
                               @Part("filename") File file);

Issue is when i am trying to add File Parameter as a Part its throwing me a error other wise if i use only @Field it works great but not working after i add @Part in it

is there a no way to use @Field and @part together in Retrofit?

If yes than tell a reason, If no tell me a proper way.

Answer: You cannot use both @FormUrlEncoded and @Multipart on a single method. An HTTP request can only have one Content-Type and both of those are content types.

@FormUrlEncoded (for android) | application/x-www-form-urlencoded(for web)

@Multipart (for android) | multipart/form-data(for web)

For your case use like this

@Multipart
@POST("registration.php")
Call<Signup> getSignupResponse(@Part("email") RequestBody email,
                               @Part("lname") RequestBody lname,
                               @Part("fname") RequestBody fname,
                               @Part("password") RequestBody password,
                               @Part MultipartBody.Part filename);

and use retrofit call like this

    File file = new File(path);
    RequestBody emailRequest = RequestBody.create(MediaType.parse("text/plain"), email);
    RequestBody lnameRequest = RequestBody.create(MediaType.parse("text/plain"), lname);
    RequestBody fnameRequest = RequestBody.create(MediaType.parse("text/plain"), fname);
    RequestBody passwordRequest = RequestBody.create(MediaType.parse("text/plain"), password);

    MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), RequestBody.create(MediaType.parse("image/*"), file));



      Call<Signup> call = qikGrubApi.upload(emailRequest, lnameRequest ,fnameRequest , passwordRequest, filePart);

            call.enqueue(new Callback<Signup>() {
                @Override
                public void onResponse(Call<Signup> call, Response<Signup> response) {
                    progress.dismiss();
                    if (response.isSuccessful()) {
                        if (response.body().getSuccess()) {
                            nextPage(response.body().getMessage());
                        } else
                            Toast.makeText(UploadActivity.this, response.body().getMessage(), Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(UploadActivity.this, "Sorry for inconvince server is down", Toast.LENGTH_SHORT).show();
                    }
                }

                @Override
                public void onFailure(Call<Signup> call, Throwable t) {
                    progress.dismiss();
                    Toast.makeText(UploadActivity.this, "Check your Internet connection", Toast.LENGTH_SHORT).show();
                }
            });
        }