Bi-weekly Intro to Sentry Demo See Sentry in action and learn how errors, performance issues, and context fit together to help you find bugs faster and ship with confidence. Join us bi-weekly on Thursdays!
Sentry Changelog Follow Twitter @SentryChangelog to stay up to date on everything from product updates to SDK changes.
Bulk Code Mappings Upload via Sentry CLI Code mappings connect your stack traces to source code, enabling features like source context, suspect commits, and stack trace linking. Previously, you had to configure them one-by-one through the Sentry UI — tedious for monorepos with dozens of modules.
You can now upload code mappings in bulk using sentry-cli:
sentry-cli code-mappings upload ./mappings.json
The JSON file pairs stack trace roots with their source paths in your repository:
[
{ "stackRoot" : "io/sentry/android/core" , "sourceRoot" : "sentry-android-core/src/main/java/io/sentry/android/core" } ,
{ "stackRoot" : "io/sentry/opentelemetry" , "sourceRoot" : "sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry" } ,
{ "stackRoot" : "io/sentry/opentelemetry" , "sourceRoot" : "sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry" }
]
Multiple mappings can share the same stack root — useful when the same package prefix exists across multiple modules (like io/sentry/opentelemetry above).
Generating Mappings for Gradle Projects
For Android/JVM projects, you can generate the mappings JSON from your Gradle build. Add this task to your root build.gradle.kts:
abstract GenerateSentryMappingsTask
class
:
DefaultTask
(
)
{
@get:InputFiles
@get:PathSensitive ( PathSensitivity. RELATIVE)
abstract val sourceDirs: ConfigurableFileCollection
@get:Internal
abstract val rootDir: DirectoryProperty
@get:OutputFile
abstract val outputFile: RegularFileProperty
@TaskAction
fun generate ( ) {
val outFile = outputFile. get ( ) . asFile
val rootPath = rootDir. get ( ) . asFile
val mappings = sourceDirs. files
. filter { it. exists ( ) && ! it. path. contains ( "/build/" ) }
. mapNotNull { srcDir ->
val packageRoot = findPackageRoot ( srcDir) ?: return @mapNotNull null
val sourceRoot = packageRoot. relativeTo ( rootPath) . path
val stackRoot = packageRoot. relativeTo ( srcDir) . path
""" {"stackRoot": " $ stackRoot ", "sourceRoot": " $ sourceRoot "}"""
}
val json = "[\n ${ mappings. joinToString ( ",\n" ) } \n]"
outFile. writeText ( json)
logger. lifecycle ( "Generated ${ mappings. size } code mappings to ${ outFile. name } " )
}
private fun findPackageRoot ( root: File) : File? {
var dir = root
while ( true ) {
val children = dir. listFiles ( ) ?: return null
if ( children. any { it. isFile } ) return dir
val subdirs = children. filter { it. isDirectory }
if ( subdirs. size == 1 ) {
dir = subdirs. single ( )
} else {
return if ( subdirs. isNotEmpty ( ) ) dir else null
}
}
}
}
val generateMappings = tasks. register< GenerateSentryMappingsTask> ( "generateSentryMappings" ) {
rootDir. set ( rootProject. layout. projectDirectory)
outputFile. set ( rootProject. layout. projectDirectory. file ( "sentry-mappings.json" ) )
}
subprojects {
plugins. withId ( "com.android.library" ) {
val android = extensions. getByType ( com. android. build. gradle. BaseExtension:: class . java)
generateMappings. configure {
sourceDirs. from ( android. sourceSets. getByName ( "main" ) . java. srcDirs)
}
}
plugins. withId ( "com.android.application" ) {
val android = extensions. getByType ( com. android. build. gradle. BaseExtension:: class . java)
generateMappings. configure {
sourceDirs. from ( android. sourceSets. getByName ( "main" ) . java. srcDirs)
}
}
plugins. withType ( JavaPlugin:: class . java) {
val java = extensions. getByType ( JavaPluginExtension:: class . java)
generateMappings. configure {
sourceDirs. from ( java. sourceSets. getByName ( "main" ) . allJava. srcDirs)
}
}
}
./gradlew generateSentryMappings
sentry-cli code-mappings upload ./sentry-mappings.json
What You Get
Source context and stack trace linking for all modules without manual setup
Suspect commits and code owners working across your entire monorepo
CI/CD friendly — keep mappings in sync as your project structure evolves
Check our docs for more details.