Skip to content

Error message upload [SC][SCEE] #6959

Description

@401wagen1
Image

How to Reproduce

reinstall SC/SCEE completely. Login via OAuth. disable all quests. Instant error message. Changeset via Vespucci etc is possible.

Expected Behavior

should not produce error after fresh install

Additional Information

Android 16 / SC v63.1 / SCEE v63.21

Activity

matkoniecz commented on Jul 28, 2026

@matkoniecz
Member

Have you send this email as requested?

westnordost commented on Jul 28, 2026

@westnordost
Member

Not necessary. Since today 11:00 AM, I got a dozen or so reports with the same stack trace.

de.westnordost.streetcomplete.data.ApiClientException: Client request(POST https://streetcomplete.app/photo-upload/upload.php) invalid: 415 Unsupported Media Type. Text: "{"error":"File type not allowed"}"
at de.westnordost.streetcomplete.data.osmnotes.PhotoServiceApiClientImpl.upload(SourceFile:42)
at de.westnordost.streetcomplete.data.osmnotes.PhotoServiceApiClientImpl$upload$1.invokeSuspend(SourceFile:12)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(SourceFile:34)
at kotlinx.coroutines.DispatchedTask.run(SourceFile:100)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(SourceFile:124)
at kotlinx.coroutines.scheduling.TaskImpl.run(SourceFile:89)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(SourceFile:586)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(SourceFile:798)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(SourceFile:717)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(SourceFile:704)
Caused by: io.ktor.client.plugins.ClientRequestException: Client request(POST https://streetcomplete.app/photo-upload/upload.php) invalid: 415 Unsupported Media Type. Text: "{"error":"File type not allowed"}"
at io.ktor.client.plugins.DefaultResponseValidationKt$addDefaultResponseValidation$1$1.invokeSuspend(SourceFile:54)
at io.ktor.client.plugins.DefaultResponseValidationKt$addDefaultResponseValidation$1$1.invoke(SourceFile:0)
at io.ktor.client.plugins.DefaultResponseValidationKt$addDefaultResponseValidation$1$1.invoke(SourceFile:0)
at io.ktor.client.plugins.HttpCallValidatorKt.HttpCallValidator$lambda$0$validateResponse(SourceFile:125)
at io.ktor.client.plugins.HttpCallValidatorKt.access$HttpCallValidator$lambda$0$validateResponse(SourceFile:1)
at io.ktor.client.plugins.HttpCallValidatorKt$HttpCallValidator$2$2.invokeSuspend(SourceFile:144)
... 8 more

I.e. it happens when attaching a photo to a note.

Nothing changed in the app, but apparently something changed with my hosting? Will investigate.

westnordost commented on Jul 28, 2026

@westnordost
Member

Hmm, the file type is application/x-empty.

self-assigned this
on Jul 28, 2026

westnordost commented on Jul 28, 2026

@westnordost
Member

Also reproducible with v63.1. So, it is very likely that it has not been caused by any change made for the app.

Server-side, the script that checks for the content type is

$photo = file_get_contents('php://input', false, null, 0, $max_content_length);
$finfo = new finfo(FILEINFO_MIME_TYPE);
$file_type = $finfo->buffer($photo);

$file_type is application/x-empty since today for StreetComplete uploads.

Testing if maybe something in PHP is broken, I executed this script (placing a picture.jpg into the same directory)

$photo = file_get_contents('picture.jpg');
$finfo = new finfo(FILEINFO_MIME_TYPE);
$file_type = $finfo->buffer($photo);

Here, $file_type is image/jpeg as expected.

🤔

westnordost commented on Jul 28, 2026

@westnordost
Member

Now I have this PHP script:

<?php
$photo = file_get_contents('php://input', false, null, 0, 5000 * 1000);
$finfo = new finfo(FILEINFO_MIME_TYPE);
$file_type = $finfo->buffer($photo);
print($file_type);
?>

and when I test via

curl -X POST 'https://www.westnordost.de/misc/test.php' --data-binary '@picture.jpg'

(having a JPEG file named picture.jpg in the same directory), it also returns image/jpeg correctly. What...

matkoniecz commented on Jul 28, 2026

@matkoniecz
Member

$file_type is application/x-empty since today for StreetComplete uploads.

are files themselves blank/empty/mangled or are these containing image data as they should be? Or are partially corrupt? Or fully fine just loading them is broken?

(sorry if my rubber ducking attempt was unhelpful)

westnordost commented on Jul 28, 2026

@westnordost
Member

I appreciate the quak

westnordost commented on Jul 28, 2026

@westnordost
Member

Yes, I see the file is empty (from the view of the server). But on the client, it is not empty. So, the issue must be in the HTTP client on Android.

mnalis commented on Jul 28, 2026

@mnalis
Member

Yes, I see the file is empty (from the view of the server). But on the client, it is not empty. So, the issue must be in the HTTP client on Android.

Or, perhaps, the tmp directory that PHP upload process uses is full on the server? (or it has lost correct permissions? or PHP got upgraded and get different defaults?)

in any case, since the problem is same with old clients which worked perfectly and whose code has not changed since it worked, to me, it would point to some operational change that happened at the server.

401wagen1 commented on Jul 28, 2026

@401wagen1
Author

I.e. it happens when attaching a photo to a note.

I did post a photo in a note. Then the message kept appearing in both apps.

westnordost commented on Jul 28, 2026

@westnordost
Member

@mnalis I too expected that, for the reasons you state.

But seems to be not the case.

The code in question on the client side is

val response = httpClient.post(baseUrl + "upload.php") {
contentType(ContentType.defaultForFilePath(file.toString()))
header("Content-Transfer-Encoding", "binary")
setBody(ByteReadChannel(fileSystem.source(file).buffered()))
expectSuccess = true
}

What's conspicuous is that the Content-Length header is not set, which is required as far as I know. This worked before. Ktor (the HTTP client library) should calculate that automatically.

(Also, a LLM chatbot tells me that the "Content-Transfer-Encoding" header is not used in HTTP but in email and thus superfluous/incorrect, so I removed it.)

westnordost commented on Jul 28, 2026

@westnordost
Member

I found a fix:

 val response = httpClient.post(baseUrl + "upload.php") { 
     contentType(ContentType.defaultForFilePath(file.toString())) 
-    setBody(ByteReadChannel(fileSystem.source(file).buffered()))
+    setBody(fileSystem.source(file).buffered().readByteArray())
     expectSuccess = true 
 } 

i.e., read the whole file into a byte array first, rather than stream it into the HTTP request. Then, Ktor has no problem to determine and set the Content-Length automatically.

The Ktor API reference mentions that for streamed body content, where the content length is unknown, Transfer-Encoding: chunked is used. However, I read that this transfer encoding is only supported in HTTP 1.1, not in HTTP/2. Now, this might explain why it started to fail today even though nothing in the client nor server was changed: If Ktor uses HTTP/2 (don't know how to check that) and also uses chunked transfer encoding anyway, it never should have worked, so a minor update to e.g. the (HTTP dependency of the) PHP runtime on the server could have broken this. Now, this is all conjecture. But I think I have at least gathered enough information to file a bug report at KTOR while in parallel I will deploy the fix to a new minor version of StreetComplete.

westnordost commented on Jul 28, 2026

@westnordost
Member
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @mnalis@matkoniecz@westnordost@401wagen1

      Issue actions