Wei-Shaw/sub2api · error · ErrDingTalkV1AppTypeMismatch

dingtalk: internal_only requires app_type=internal

Error message

dingtalk: internal_only requires app_type=internal

What it means

Returned by ValidateDingTalkConfig in backend/internal/config/validate_dingtalk.go when CorpRestrictionPolicy is "internal_only" but AppType is not "internal". The internal_only security model (Plan A) relies on DingTalk 'enterprise internal app' semantics — only employees of the app's own corporation can complete OAuth — so the config must declare app_type=internal. The old requirement for a non-empty InternalCorpID (V3) was removed; the field remains optional and unused for constraint.

Source

Thrown at backend/internal/config/validate_dingtalk.go:13

// Package config 包含钉钉连接配置的校验逻辑。
//
// internal_only 模式安全模型(方案 A):
// 不再要求 admin 填写 InternalCorpID 做二次 corpID 比对。
// 安全边界由钉钉"企业内部应用"类型本身保证——只有应用所属企业的员工才能完成 OAuth,
// 因此 ValidateDingTalkConfig 只要求 app_type=internal(V1),不再要求 InternalCorpID 非空(原 V3 已删除)。
// InternalCorpID 字段保留,admin 可选填;若填写,checkDingTalkCorpAllowed 不会使用它做约束。
package config

import "errors"

var (
	ErrDingTalkV1AppTypeMismatch = errors.New("dingtalk: internal_only requires app_type=internal")
	ErrDingTalkV4InvalidAppKind  = errors.New("dingtalk: dingtalk_app_kind must be internal_app")
)

func ValidateDingTalkConfig(cfg DingTalkConnectConfig) error {
	if !cfg.Enabled {
		return nil
	}
	if cfg.DingTalkAppKind != "internal_app" {
		return ErrDingTalkV4InvalidAppKind
	}
	if cfg.CorpRestrictionPolicy == "internal_only" {
		if cfg.AppType != "internal" {
			return ErrDingTalkV1AppTypeMismatch
		}
	}
	return nil
}

View on GitHub (pinned to 073e92d171)

Solutions

  1. Set app_type=internal in the dingtalk config section (and keep dingtalk_app_kind=internal_app).
  2. If you intentionally use a non-internal app, change corp_restriction_policy away from "internal_only" (e.g. a policy supported for that app type).
  3. If you relied on the old InternalCorpID check, remove it — it is no longer required nor used as a constraint.
  4. Re-run the config validator / restart and confirm the error is gone.

Example fix

# before
dingtalk:
  enabled: true
  dingtalk_app_kind: internal_app
  corp_restriction_policy: internal_only
  app_type: ""        # or "corp"
  internal_corp_id: "dingxxxx"   # legacy, no longer sufficient

# after
dingtalk:
  enabled: true
  dingtalk_app_kind: internal_app
  corp_restriction_policy: internal_only
  app_type: internal
Defensive patterns

Strategy: validation

Validate before calling

func validDingTalkCfg(cfg DingTalkConnectConfig) error {
    if !cfg.Enabled { return nil }
    if cfg.DingTalkAppKind != "internal_app" { return ErrDingTalkV4InvalidAppKind }
    if cfg.CorpRestrictionPolicy == "internal_only" && cfg.AppType != "internal" {
        return ErrDingTalkV1AppTypeMismatch
    }
    return nil
}

Try / catch

if err := config.ValidateDingTalkConfig(cfg); err != nil {
    if errors.Is(err, config.ErrDingTalkV1AppTypeMismatch) {
        log.Fatalf("config: set dingtalk app_type=internal (or change corp_restriction_policy): %v", err)
    }
    log.Fatalf("config: invalid dingtalk config: %v", err)
}

Prevention

When it happens

Trigger: Starting the backend with dingtalk config where corp_restriction_policy=internal_only and app_type is empty, "corp", or any value other than "internal"; also fails startup if dingtalk_app_kind != "internal_app" (ErrDingTalkV4InvalidAppKind, checked first). Only cfg.Enabled=true configurations are validated.

Common situations: Copying a config from an older deployment that used app_type values like "corp" or left app_type unset; upgrading to the version where V3 corpID comparison was replaced by V1 app_type check; mixing settings from a third-party (服务商) DingTalk app with internal_only policy.

Related errors


AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15). Data as JSON: /api/errors/6e675bacaf98714b. Report an issue: GitHub.