diff --git generic/tclScan.c generic/tclScan.c
index f37f596a1c..ba3d90f113 100644
--- generic/tclScan.c
+++ generic/tclScan.c
@@ -305,7 +305,7 @@ ValidateFormat(
 	     * format string.
 	     */
 
-	    value = strtoul(format-1, &end, 10);	/* INTL: "C" locale. */
+	    unsigned long ul = strtoul(format-1, &end, 10);	/* INTL: "C" locale. */
 	    if (*end != '$') {
 		goto notXpg;
 	    }
@@ -315,17 +315,20 @@ ValidateFormat(
 	    if (gotSequential) {
 		goto mixedXPG;
 	    }
-	    objIndex = value - 1;
-	    if ((objIndex < 0) || (numVars && (objIndex >= numVars))) {
+	    if (ul == 0 || ul >= INT_MAX) {
+		goto badIndex;
+	    }
+	    objIndex = (int) ul - 1;
+	    if (numVars && (objIndex >= numVars)) {
 		goto badIndex;
 	    } else if (numVars == 0) {
 		/*
 		 * In the case where no vars are specified, the user can
 		 * specify %9999$ legally, so we have to consider special
-		 * rules for growing the assign array. 'value' is guaranteed
-		 * to be > 0.
+		 * rules for growing the assign array. 'ul' is guaranteed
+		 * to be > 0 and < INT_MAX as per checks above.
 		 */
-		xpgSize = (xpgSize > value) ? xpgSize : value;
+		xpgSize = (xpgSize > (int)ul) ? xpgSize : (int)ul;
 	    }
 	    goto xpgCheckDone;
 	}
diff --git tests/scan.test tests/scan.test
index 300335e889..cd2ba6392f 100644
--- tests/scan.test
+++ tests/scan.test
@@ -852,6 +852,11 @@ test scan-13.8 {Tcl_ScanObjCmd, inline XPG case lots of arguments} {
     set msg [scan "10 20 30" {%100$d %5$d %200$d}]
     list [llength $msg] [lindex $msg 99] [lindex $msg 4] [lindex $msg 199]
 } {200 10 20 30}
+test scan-13.9 {Tcl_ScanObjCmd, inline XPG case limit error} -body {
+    # Note this applies to 64-bit builds as well so long as max number of
+    # command line arguments allowed for scan command is INT_MAX
+    scan abc {%2147483648$s}
+} -result {"%n$" argument index out of range} -returnCodes error
 
 # scan infinities - not working
 
