plandex-ai/plandex · error

error getting num tokens: %v

Error message

error getting num tokens: %v

What it means

UpdateContexts computes the token count for each context being updated. For image contexts it calls shared.GetImageTokens, which base64-decodes the image body and parses its header to compute width/height-based token estimates. This error wraps any failure from that call — most commonly invalid base64 data or an undecodable/corrupt image header.

Source

Thrown at app/server/db/context_helpers_update.go:176

					return
				}
				// log.Println("Got context", context.Id, "numTokens", context.NumTokens)
			}

			mu.Lock()
			defer mu.Unlock()

			contextsById[id] = context
			updatedContexts = append(updatedContexts, context.ToApi())

			if context.ContextType != shared.ContextMapType {
				var updateNumTokens int
				var err error

				if context.ContextType == shared.ContextImageType {
					updateNumTokens, err = shared.GetImageTokens(params.Body, context.ImageDetail)
					if err != nil {
						errCh <- fmt.Errorf("error getting num tokens: %v", err)
						return
					}
				} else {
					updateNumTokens = shared.GetNumTokensEstimate(params.Body)
					// log.Println("len(params.Body)", len(params.Body))
				}

				// log.Println("Updating context", id, "updateNumTokens", updateNumTokens)

				tokenDiff := updateNumTokens - context.NumTokens
				tokenDiffsById[id] = tokenDiff
				aggregateTokensDiff += tokenDiff
				totalTokens += tokenDiff
				totalPlannerTokens += tokenDiff
				if !context.AutoLoaded {
					totalBasicPlannerTokens += tokenDiff
					aggregateBasicTokensDiff += tokenDiff
				}

View on GitHub (pinned to e2d772072e)

Solutions

  1. Strip the 'data:image/...;base64,' prefix and other non-base64 characters from params.Body before sending
  2. Verify the file is a real, complete image and in a format with a registered Go decoder (jpeg, png, gif); re-upload if truncated
  3. Check server logs for 'failed to decode base64 image data' vs 'failed to decode image config' to pinpoint which stage failed
  4. If using a newer format, ensure the corresponding image decoder package is imported for side effects in shared/images.go

Example fix

// before
body := "data:image/png;base64,iVBORw0KGgo..."
// after
body := strings.TrimPrefix(raw, "data:image/png;base64,")
if _, err := base64.StdEncoding.DecodeString(body); err != nil { return fmt.Errorf("invalid base64 image body") }
Defensive patterns

Strategy: validation

Validate before calling

func validImageBody(b string) bool {
    b = stripDataURIPrefix(b)
    data, err := base64.StdEncoding.DecodeString(b)
    if err != nil || len(data) == 0 { return false }
    _, _, err = image.DecodeConfig(bytes.NewReader(data))
    return err == nil
}
// call before UpdateContexts for each image context

Type guard

func isDecodableImage(body string) (ok bool) {
    defer func() { if recover() != nil { ok = false } }()
    return validImageBody(body)
}

Try / catch

if _, err := shared.GetImageTokens(params.Body, detail); err != nil {
    log.Printf("skipping invalid image context %s: %v", id, err)
    return nil // drop the context instead of failing the batch
}

Prevention

When it happens

Trigger: An UpdateContexts request supplies an image-type context whose params.Body is not valid standard base64 (e.g. data-URI prefix like 'data:image/png;base64,' left in, whitespace/newlines, URL-safe base64) or whose decoded bytes are not a recognizable image format (image.DecodeConfig fails on truncated/corrupt/unsupported formats).

Common situations: Client sends a data URL instead of raw base64; image was truncated by an upload size limit; user attached a non-image file renamed to .png; an image format without a registered decoder (e.g. webp/avif without the decoder imported) is passed through.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/d22c3ecb35864669. Report an issue: GitHub.