Encontré y resolví este problema por mi cuenta después de que un cliente lo reportara. Usé IA para generar el informe del error porque estaba perezoso y lo hace mejor que yo; además, mientras lo hacía, detectó un error en el núcleo, que verifiqué manualmente.
No es nada grave, pero podría tener sentido comprobar si este patrón está causando problemas en otros lugares también.
Todo comenzó cuando alguien habilitó enable_direct_s3_uploads sin haber configurado y activado S3, lo que rompió todas las cargas, devolviendo errores 500 en segundo plano. Pensé que el error 500 merecía una investigación.
Resumen
Cuando enable_direct_s3_uploads está habilitado mientras el almacén de carga activo es local, intentar una carga puede provocar:
NoMethodError (undefined method 'signed_request_for_temporary_upload' for an instance of FileStore::LocalStore)
app/services/external_upload_manager.rb:34:in 'ExternalUploadManager.create_direct_upload'
lib/external_upload_helpers.rb:56:in 'ExternalUploadHelpers#generate_presigned_put'
app/controllers/application_controller.rb:443:in 'block in ApplicationController#with_resolved_locale'
app/controllers/application_controller.rb:443:in 'ApplicationController#with_resolved_locale'
Esto parece ser causado por una combinación de un estado de configuración inválido y una sobrescritura de la callback before_action.
Configuración
El estado problemático es, en esencia:
SiteSetting.enable_direct_s3_uploads == true
SiteSetting.enable_s3_uploads == false
Discourse.store.is_a?(FileStore::LocalStore) == true
Para el almacenamiento local, Discourse.store no implementa:
signed_request_for_temporary_upload
lo cual es esperable, ya que esa operación solo tiene sentido para un almacén externo/S3.
Comportamiento esperado
Si las cargas directas a S3 están habilitadas mientras el almacén activo es local, la solicitud debería ser rechazada limpiamente por external_store_check.
Idealmente, también se debería prevenir o validar la combinación inválida de la configuración del sitio.
Comportamiento actual
La solicitud llega a ExternalUploadManager.create_direct_upload, que llama a:
store.signed_request_for_temporary_upload(...)
en un FileStore::LocalStore, lo que resulta en un NoMethodError.
Posible causa
ExternalUploadHelpers registra la siguiente callback:
before_action :external_store_check,
only: %i[
generate_presigned_put
complete_external_upload
create_multipart
batch_presign_multipart_parts
complete_multipart
abort_multipart
]
Esto debería impedir que la solicitud llegue al código de carga externa cuando el almacén es local.
Sin embargo, UploadsController registra posteriormente otra callback utilizando el mismo método de filtro:
before_action :external_store_check,
only: %i[_show_secure_deprecated show_secure]
Rails trata la registración repetida de la misma callback como una redefinición, por lo que esta última parece reemplazar las condiciones only: anteriores.

Como resultado, external_store_check ya no se ejecuta para generate_presigned_put, permitiendo que un almacén local llegue a la ruta de código de carga directa.
Reproducción
- Configura una instancia de Discourse con almacenamiento de carga local.
- Asegúrate de que:
SiteSetting.enable_s3_uploads = false
SiteSetting.enable_direct_s3_uploads = true
- Intenta cargar un archivo.
- Observa el
NoMethodErrordesdeFileStore::LocalStore.
Una verificación del estado en la consola se puede hacer con:
[
SiteSetting.enable_direct_s3_uploads,
SiteSetting.enable_s3_uploads,
Discourse.store.class,
Discourse.store.external?
]
Solución sugerida
Las registraciones de callbacks no deberían sobrescribirse entre sí.
Por ejemplo, incluye las acciones de carga segura en la callback existente external_store_check:
before_action :external_store_check,
only: %i[
generate_presigned_put
complete_external_upload
create_multipart
batch_presign_multipart_parts
complete_multipart
abort_multipart
_show_secure_deprecated
show_secure
]
Alternativamente, usa un método de callback separado para las acciones de carga segura.
También podría ser valioso agregar validaciones para que enable_direct_s3_uploads no pueda habilitarse mientras las cargas S3/externas estén deshabilitadas.
Solución temporal
Para sitios que utilizan almacenamiento de carga local:
SiteSetting.enable_direct_s3_uploads = false
previene el error.