You have integrated the Smaato SDK, your build succeeds, and the app launches. But where the ad should be, there is only whitespace. In your Logcat or Xcode console, you see the dreaded, vague response: NO_AD_AVAILABLE or the internal Error 42.
For mobile developers and monetization managers, this is a critical revenue leak. When the SDK returns "No Fill," it isn't just a technical glitch; it is a failure of the auction logic or the transport layer.
This guide moves beyond generic advice like "check your internet connection." We will dissect the root causes of Smaato delivery failures, analyze the OpenRTB handshake mechanics, and provide the specific Kotlin and Swift implementations required to debug and resolve these errors.
The Root Cause: What "No Fill" Actually Means
To fix the error, you must understand the architecture of a mobile ad request. When your app calls loadAd(), the Smaato SDK initiates a complex chain of events:
- The Bid Request: The SDK packages device data (User Agent, IP, Geo, IDFA/GAID) and sends it to the Smaato Exchange (SPX).
- The Auction: SPX broadcasts this request to Demand Side Platforms (DSPs).
- The Response: DSPs decide in milliseconds whether to bid.
Error 42 (No Ad Available) occurs when this chain breaks at step 3. The server received your request, but zero advertisers were willing to buy the impression.
While this sounds like a business problem, it is frequently caused by technical misconfiguration that makes your inventory appear "invalid" to buyers. Common technical culprits include:
- CMP/GDPR Mismatches: Missing consent strings in the header.
- ATS/Cleartext Blocking: The OS blocking non-HTTPS creative assets.
- Geo-IP Mismatches: Testing a "US-Only" AdSpace from a development machine in Europe or Asia.
Troubleshooting Phase 1: The Code Implementation
Before tweaking dashboard settings, you must ensure your implementation captures the exact reason for failure. A silent failure is impossible to debug.
Android Implementation (Kotlin)
In modern Android development, you must handle the BannerViewDelegate or InterstitialAdDelegate explicitly. Do not assume the View will handle errors visually.
Ensure your AndroidManifest.xml allows cleartext traffic if you are accepting older creatives or testing:
<!-- AndroidManifest.xml -->
<application
android:usesCleartextTraffic="true"
...>
</application>
Here is the robust, production-ready Kotlin implementation for initialization and error logging:
import android.util.Log
import com.smaato.sdk.banner.widget.BannerError
import com.smaato.sdk.banner.widget.BannerView
import com.smaato.sdk.banner.widget.BannerViewDelegate
import com.smaato.sdk.core.SmaatoSdk
import com.smaato.sdk.core.Config
class AdManager {
companion object {
private const val PUBLISHER_ID = "11000XXXX" // Replace with your ID
private const val ADSPACE_ID = "130XXXXX" // Replace with your ID
}
fun initializeSmaato(context: android.content.Context) {
// 1. Configure for detailed logging during development
val config = Config.builder()
.setLogLevel(com.smaato.sdk.core.log.LogLevel.DEBUG)
.setHttpsOnly(true) // Enforce HTTPS to satisfy strict DSPs
.build()
SmaatoSdk.init(context, config, PUBLISHER_ID) {
Log.d("AdTech", "Smaato SDK Initialized successfully")
}
}
fun loadBanner(bannerView: BannerView) {
// 2. Set the delegate BEFORE loading
bannerView.setBannerViewDelegate(object : BannerViewDelegate {
override fun onAdLoaded(bannerView: BannerView) {
Log.d("AdTech", "Impression Recorded: Ad Loaded.")
}
override fun onAdFailedToLoad(bannerView: BannerView, bannerError: BannerError) {
// CRITICAL: Log the specific enum and description
// Common Errors: NO_AD_AVAILABLE, NETWORK_ERROR, CONFIGURATION_ERROR
Log.e("AdTech", "Load Failed. Type: ${bannerError.errorType}, Msg: ${bannerError.description}")
// Logic to fallback to another network (Waterfall) goes here
}
override fun onAdImpression(bannerView: BannerView) {}
override fun onAdClicked(bannerView: BannerView) {}
override fun onAdTTLExpired(bannerView: BannerView) {}
})
// 3. Load the specific AdSpace
bannerView.loadAd(ADSPACE_ID, com.smaato.sdk.banner.ad.BannerAdSize.XX_LARGE_320x50)
}
}
iOS Implementation (Swift)
On iOS, App Transport Security (ATS) is the number one cause of fill issues for valid ad requests. If a DSP returns an ad with an HTTP asset tracking pixel, iOS will block the resource, and the Smaato SDK may report this as a load failure.
Ensure your Info.plist includes:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Here is the Swift implementation utilizing delegates for granular error inspection:
import UIKit
import SmaatoSDK
class AdViewController: UIViewController, SMABannerViewDelegate {
var bannerView: SMABannerView?
let kPublisherId = "11000XXXX"
let kAdSpaceId = "130XXXXX"
override func viewDidLoad() {
super.viewDidLoad()
setupSmaato()
}
func setupSmaato() {
// 1. explicit config for GDPR/CCPA logic if necessary
let config = SMAConfiguration(publisherId: kPublisherId)
config.logLevel = .debug // Essential for troubleshooting
SmaatoSDK.init(config: config)
// 2. Instantiate and Load
bannerView = SMABannerView(adSpaceId: kAdSpaceId)
bannerView?.delegate = self
bannerView?.autoreloadInterval = .veryShort // For testing only
if let banner = bannerView {
view.addSubview(banner)
banner.load()
}
}
// MARK: - SMABannerViewDelegate
func bannerViewDidDidLoad(_ bannerView: SMABannerView) {
print("AdTech: Banner loaded successfully.")
}
func bannerView(_ bannerView: SMABannerView, didFailWithError error: Error) {
// 3. Deep dive into the NSError
let nsError = error as NSError
print("AdTech: Failed to load. Code: \(nsError.code)")
print("AdTech: Description: \(nsError.localizedDescription)")
// Code 42 often maps to 'No Fill' or 'Network Error' depending on SDK version
if nsError.code == 42 || nsError.localizedDescription.contains("No ad") {
handleNoFill()
}
}
func handleNoFill() {
print("AdTech: Logic for waterfall fallback or hiding the view")
}
}
Troubleshooting Phase 2: Configuration & SPX Portal
If your code is correct (listeners are attached, IDs are strings, logs confirm the request was sent) but you still receive NO_AD_AVAILABLE, the issue lies in the configuration.
1. The "Test Mode" Misconception
Unlike Google AdMob, which often allows you to use a generic test ID, Smaato works differently.
- The Trap: You cannot simply send a request to a live AdSpace ID from an emulator or a device that has not been flagged for testing.
- The Fix: You must use Smaato's dedicated Test Publisher and AdSpace IDs provided in their documentation to verify connectivity. Only after the test IDs render an ad successfully should you switch to your live IDs.
- Live Testing: If you must test a live ID, you must use a VPN to match the geography of the line items targeted in SPX. If your AdSpace is targeted to "US Only" and you are debugging from Berlin, you will get Error 42.
2. eCPM Floors are Too High
This is the most common reason for No Fill on live accounts.
- The Logic: If you set a hard floor of $2.00 eCPM in the SPX portal, Smaato discards every bid under $2.00. If the highest bid is $1.99, Smaato returns
NO_AD_AVAILABLE. - The Fix: During the initial launch, set your eCPM floors to Optimized or $0.01. Allow the market to determine the price to verify technical integration. Once fill rate stabilizes, increment the floor gradually.
3. CMP and TCF 2.0 Strings
If your traffic is from the EEA (European Economic Area) or UK, and you do not send a valid TCF 2.0 consent string, Smaato (and its demand partners) will drop the request immediately to avoid GDPR fines.
- The Check: Look at your logcat/console logs. Do you see
TP: Smaatoinside your CMP consent string? - The Fix: Ensure your CMP (Consent Management Platform) is initialized before the Smaato SDK initialization. Smaato automatically reads the
IABTCF_TCStringfromSharedPreferences(Android) orUserDefaults(iOS). If that string is empty whenloadAd()is called, the fill rate will be zero for EU users.
4. Dimension Mismatches
The AdSpace size configured in SPX must match the code request strictly.
- If SPX expects a 320x50 (Banner).
- And your code requests a 300x250 (Medium Rectangle).
- Result: The server rejects the request. There is no "fuzzy matching" for dimension sizes in rigid RTB environments.
Summary Checklist
If you are stuck in an "Error 42" loop, follow this sequence to exit:
- Switch to Smaato Demo IDs. Does the ad load?
- Yes: Your code is fine. The issue is your SPX account settings (Floors, Geo-targeting).
- No: Your code or network security config (ATS/Cleartext) is broken.
- Check Floors. Lower them to $0.01 temporarily.
- Validate Dimensions. Ensure
BannerAdSizein code matches the AdSpace format in SPX. - Verify Consent. If testing in the EU, ensure the CMP has generated a valid string before the ad request fires.
By isolating the error between the Code Layer (SDK integration) and the Business Layer (SPX configuration), you can turn a generic "No Fill" error into a solvable configuration task.